UPDATE: I am able to reproduce this "Premature end of script headers: wsgihandler.py" error with a slightly modified app based on the image blog in the web2py book. I modified it so that images are shown in the index page, instead of having just the titles listed.
Testing using: ab -kc 100 -t 20 https://domain.com/imageblog/default/index/ If I am simply doing this, there's no error. BUT when I was running this test, while simultaneously browsing the website, clicking on images, etc., then the error appeared, consistently. Monitoring available RAM while running this test showed system RAM peeked at about 500MB, still a few hundreds MB available. This is on the same server as the other app. VPS. 768MB RAM. 1GB RAM Burstable. About 129 apache2 processes running and 11,000 files openning. sudo ps aux | grep apache2 | wc 129 1675 11847 sudo lsof | wc 10982 106870 1313126 ================================================= Here's a typical Apache Bench result: Server Software: Apache/2.2.9 Server Port: 443 SSL/TLS Protocol: TLSv1/SSLv3,DHE-RSA-AES256-SHA,1024,256 Document Path: /imageblog/default/index/ Document Length: 13130 bytes Concurrency Level: 100 Time taken for tests: 20.004 seconds Complete requests: 588 Failed requests: 15 (Connect: 0, Receive: 0, Length: 15, Exceptions: 0) Write errors: 0 Non-2xx responses: 15 Keep-Alive requests: 573 Total transferred: 7875930 bytes HTML transferred: 7564607 bytes Requests per second: 29.39 [#/sec] (mean) Time per request: 3401.991 [ms] (mean) Time per request: 34.020 [ms] (mean, across all concurrent requests) Transfer rate: 384.50 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 912 2146.5 0 7265 Processing: 86 2234 1303.3 2781 4427 Waiting: 85 2136 1281.0 2759 4316 Total: 86 3146 2410.6 3017 10478 Percentage of the requests served within a certain time (ms) 50% 3017 66% 3222 75% 3939 80% 4029 90% 7089 95% 9409 98% 10098 99% 10223 100% 10478 (longest request) ================================================= Relevant excerpt of db.py: db = DAL('postgres://username:password@localhost:5432/imageblog', pool_size=20) ================================================= Model: db.define_table('image', Field('title'), Field('file', 'upload')) db.define_table('comment', Field('image_id', db.image), Field('author'), Field('email'), Field('body', 'text')) db.image.title.requires = IS_NOT_IN_DB(db, db.image.title) db.comment.image_id.requires = IS_IN_DB(db, db.image.id, '%(title)s') db.comment.author.requires = IS_NOT_EMPTY() db.comment.email.requires = IS_EMAIL() db.comment.body.requires = IS_NOT_EMPTY() ================================================= Controller: def index(): images = db().select(db.image.ALL, orderby=db.image.title) return dict(images=images) @auth.requires_login() def add(): form = crud.create(db.image, next = URL('index')) return dict(form=form) def show(): image = db(db.image.id==request.args(0)).select().first() form = SQLFORM(db.comment) form.vars.image_id = image.id if form.accepts(request.vars, session): response.flash = 'your comment is posted' comments = db(db.comment.image_id==image.id).select() return dict(image=image, comments=comments, form=form) def user(): """ exposes: http://..../[app]/default/user/login http://..../[app]/default/user/logout http://..../[app]/default/user/register http://..../[app]/default/user/profile http://..../[app]/default/user/retrieve_password http://..../[app]/default/user/change_password use @auth.requires_login() @auth.requires_membership('group name') @auth.requires_permission('read','table name',record_id) to decorate functions that need access control """ return dict(form=auth()) def download(): """ allows downloading of uploaded files http://..../[app]/default/download/[filename] """ return response.download(request,db) def call(): """ exposes services. for example: http://..../[app]/default/call/jsonrpc decorate with @services.jsonrpc the functions to expose supports xml, json, xmlrpc, jsonrpc, amfrpc, rss, csv """ session.forget() return service() ================================================= View (index.html) {{extend 'layout.html'}} <h1>Current Images</h1> {{for image in images:}} <div class="entry" style="padding:1em; border-bottom:1px dashed #999;"> <div class="entrytitle" style="font-size:150%;">{{=A(image.title, _href=URL("show", args=image.id))}}</div> <div class="entryimage"><img src="{{=URL('download', args=image.file)}}"></div> </div> {{pass}} ================================================= Apache's site-enabled/000-default: <VirtualHost *:80> WSGIDaemonProcess web2py user=username group=username \ display-name=%{GROUP} WSGIProcessGroup web2py WSGIScriptAlias / /home/username/web2py/wsgihandler.py Alias /awstats-icon/ /usr/share/awstats/icon/ <Directory /usr/share/awstats/icon> Options None AllowOverride None Order allow,deny Allow from all </Directory> <Directory /home/username/web2py> AllowOverride None Order Allow,Deny Deny from all <Files wsgihandler.py> Allow from all </Files> </Directory> AliasMatch ^/([^/]+)/static/(.*) \ /home/username/web2py/applications/$1/static/$2 <Directory /home/username/web2py/applications/*/static/> Options -Indexes Order Allow,Deny Allow from all </Directory> <Location /admin> Deny from all </Location> <LocationMatch ^/([^/]+)/appadmin> Deny from all </LocationMatch> CustomLog /var/log/apache2/access.log common ErrorLog /var/log/apache2/error.log </VirtualHost>