<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: nginx</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/nginx.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2021-09-24T18:44:12+00:00</updated><author><name>Simon Willison</name></author><entry><title>New tool: an nginx playground</title><link href="https://simonwillison.net/2021/Sep/24/new-tool-an-nginx-playground/#atom-tag" rel="alternate"/><published>2021-09-24T18:44:12+00:00</published><updated>2021-09-24T18:44:12+00:00</updated><id>https://simonwillison.net/2021/Sep/24/new-tool-an-nginx-playground/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://jvns.ca/blog/2021/09/24/new-tool--an-nginx-playground/"&gt;New tool: an nginx playground&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Julia Evans built a sandbox tool for interactively trying out an nginx configuration and executing test requests through it. I love this kind of tool, and Julia’s explanation of how they built it using a tiny fly.io instance and a network namespace to reduce the amount of damage any malicious usage could cause is really interesting.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/julia-evans"&gt;julia-evans&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/fly"&gt;fly&lt;/a&gt;&lt;/p&gt;



</summary><category term="nginx"/><category term="security"/><category term="julia-evans"/><category term="fly"/></entry><entry><title>Weeknotes: Getting my personal Dogsheep up and running again</title><link href="https://simonwillison.net/2021/Aug/22/weeknotes-dogsheep/#atom-tag" rel="alternate"/><published>2021-08-22T04:46:13+00:00</published><updated>2021-08-22T04:46:13+00:00</updated><id>https://simonwillison.net/2021/Aug/22/weeknotes-dogsheep/#atom-tag</id><summary type="html">
    &lt;p&gt;I gave a talk about &lt;a href="https://dogsheep.github.io/"&gt;Dogsheep&lt;/a&gt; at Noisebridge's &lt;a href="https://www.noisebridge.net/wiki/Five_Minutes_of_Fame_2021_08_19"&gt;Five Minutes of Fame&lt;/a&gt; on Thursday. Just one problem: my regular Dogsheep demo was broken, so I ended up building it from scratch again. In doing so I fixed a few bugs in some Dogsheep tools.&lt;/p&gt;
&lt;h4&gt;Dogsheep on a Digital Ocean droplet&lt;/h4&gt;
&lt;p&gt;The latest iteration of my personal Dogsheep runs on a $20/month 4GB/2CPU &lt;a href="https://www.digitalocean.com/"&gt;Digital Ocean&lt;/a&gt; Droplet running Ubuntu 20.04 LTS.&lt;/p&gt;
&lt;p&gt;It runs a private Datasette instance and a bunch of cron jobs to fetch data from Twitter, GitHub, Foursquare Swarm, Pocket and Hacker News.&lt;/p&gt;
&lt;p&gt;It also has copies of my Apple Photos and Apple HealthKit data which I upload manually - plus a &lt;a href="https://simonwillison.net/2019/Sep/20/weeknotes-design-thinking-genome-sqlite/#genome-to-sqlite"&gt;copy of my genome&lt;/a&gt; for good measure.&lt;/p&gt;
&lt;p&gt;Some abbreviated notes on how I set it up, copied from a private GitHub Issues thread:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a new Ubuntu droplet, and configure its IP address as the A record for &lt;code&gt;dogsheep.simonwillison.net&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Python 3 and NGINX and SQLite: &lt;code&gt;apt-get install python3 python3-venv nginx sqlite -y&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;a href="https://letsencrypt.org/"&gt;letsencrypt&lt;/a&gt; to get an HTTPS certificate for it: &lt;code&gt;apt-get update&lt;/code&gt; and then
&lt;code&gt;apt install certbot python3-certbot-nginx -y&lt;/code&gt;, then &lt;code&gt;certbot --nginx -d dogsheep.simonwillison.net&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I had to remove the &lt;code&gt;ipv6only=on;&lt;/code&gt; bit from the NGINX configuration due to &lt;a href="https://github.com/certbot/certbot/issues/5550#issuecomment-367971137"&gt;this bug&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Created a &lt;code&gt;dogsheep&lt;/code&gt; user, &lt;code&gt;useradd -s /bin/bash -d /home/dogsheep/ -m -G&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;As that user, created a virtual environment: &lt;code&gt;python3 -mvenv datasette-venv&lt;/code&gt; and then &lt;code&gt;datasette-venv/bin/pip install wheel&lt;/code&gt; and &lt;code&gt;datasette-venv/bin/pip install datasette datasette-auth-passwords&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Created a &lt;code&gt;/etc/systemd/system/datasette.service&lt;/code&gt; file with &lt;a href="https://gist.github.com/simonw/0653b6177c6f12caa16530da4c56646f"&gt;this contents&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Created a set of blank SQLite database files &lt;a href="https://til.simonwillison.net/sqlite/enabling-wal-mode"&gt;in WAL mode&lt;/a&gt; in &lt;code&gt;/home/dogsheep&lt;/code&gt; using the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for f in beta.db twitter.db healthkit.db github.db \
  swarm.db photos.db genome.db simonwillisonblog.db \
  pocket.db hacker-news.db memories.db
do
  sqlite3 $f vacuum
  # And enable WAL mode:
  sqlite3 $f 'PRAGMA journal_mode=WAL;'
done
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Started the Datasette service: &lt;code&gt;service datasette start&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configured NGINX to proxy to localhost port 8001, using &lt;a href="https://docs.datasette.io/en/stable/deploying.html#nginx-proxy-configuration"&gt;this configuration&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It's a few more steps than I'd like, but the end result was a &lt;a href="https://datasette.io/plugins/datasette-auth-passwords"&gt;password-protected&lt;/a&gt; Datasette instance running against a bunch of SQLite database files on my new server.&lt;/p&gt;
&lt;p&gt;With Datasette up and running, the next step was to start loading in data.&lt;/p&gt;
&lt;h4&gt;Importing my tweets&lt;/h4&gt;
&lt;p&gt;I started with Twitter. I dropped my Twitter API access credentials into an &lt;code&gt;auth.json&lt;/code&gt; file (as &lt;a href="https://datasette.io/tools/twitter-to-sqlite#user-content-authentication"&gt;described here&lt;/a&gt;) and ran the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;source /home/dogsheep/datasette-venv/bin/activate
pip install twitter-to-sqlite
twitter-to-sqlite user-timeline /home/dogsheep/twitter.db \
  -a /home/dogsheep/auth.json
@simonw [###############################-----] 26299/29684 00:02:06
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That pulled in all 29,684 of my personal tweets.&lt;/p&gt;
&lt;p&gt;(Actually, first it broke with an error, exposing a &lt;a href="https://github.com/dogsheep/twitter-to-sqlite/issues/57"&gt;bug that had already been reported&lt;/a&gt;. I shipped &lt;a href="https://github.com/dogsheep/twitter-to-sqlite/releases/tag/0.21.4"&gt;a fix for that&lt;/a&gt; and tried again and it worked.)&lt;/p&gt;
&lt;p&gt;Favourited tweets were a little harder - I have 39,904 favourited tweets, but the Twitter API only returns the most recent 3,200. I grabbed those more recent ones with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;twitter-to-sqlite favorites /home/dogsheep/twitter.db \
  -a /home/dogsheep/auth.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I requested &lt;a href="https://help.twitter.com/en/managing-your-account/how-to-download-your-twitter-archive"&gt;my Twitter archive&lt;/a&gt;, waited 24 hours and uploaded the resulting &lt;code&gt;like.js&lt;/code&gt; file to the server, then ran:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;twitter-to-sqlite import twitter.db /tmp/like.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gave me an &lt;code&gt;archive_like&lt;/code&gt; table with the data from that file - but it wasn't the full tweet representation, just the subset that Twitter expose in the archive export.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://datasette.io/tools/twitter-to-sqlite#user-content-importing-data-from-your-twitter-archive"&gt;README&lt;/a&gt; shows how to inflate those into full tweets:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;twitter-to-sqlite statuses-lookup twitter.db \
  --sql='select tweetId from archive_like' \
  --skip-existing
Importing 33,382 tweets [------------------------------------] 0% 00:18:28
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once that was done I wrote additional records into the &lt;code&gt;favorited_by&lt;/code&gt; table like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sqlite3 twitter.db '
  INSERT OR IGNORE INTO favorited_by (tweet, user)
  SELECT tweetId, 12497 FROM archive_like
'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(12497 is my Twitter user ID.)&lt;/p&gt;
&lt;p&gt;I also came up with a SQL view that lets me see just media attached to tweets:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sqlite-utils create-view twitter.db media_details "
select
  json_object('img_src', media_url_https, 'width', 400) as img,
  tweets.full_text,
  tweets.created_at,
  tweets.id as tweet_id,
  users.screen_name,
  'https://twitter.com/' || users.screen_name || '/status/' || tweets.id as tweet_url
from
  media
  join media_tweets on media.id = media_tweets.media_id
  join tweets on media_tweets.tweets_id = tweets.id
  join users on tweets.user = users.id
order by
  tweets.id desc
"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I can visit &lt;code&gt;/twitter/media_details?_where=tweet_id+in+(select+tweet+from+favorited_by+where+user+=+12497)&lt;/code&gt; to see the most recent media tweets that I've favourited!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2021/liked-media-details.jpg" alt="media_details view showing Twitter media I have liked" data-canonical-src="https://static.simonwillison.net/static/2021/liked-media-details.jpg" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;h4&gt;Swarm checkins&lt;/h4&gt;
&lt;p&gt;Swarm checkins were a lot easier. I needed my previously-created Foursquare API token, and &lt;a href="https://datasette.io/tools/swarm-to-sqlite"&gt;swarm-to-sqlite&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install swarm-to-sqlite
swarm-to-sqlite /home/dogsheep/swarm.db --token=...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gave me a full table of my Swarm checkins, which I can visualize using &lt;a href="https://datasette.io/plugins/datasette-cluster-map"&gt;datasette-cluster-map&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2021/dogsheep-swarm-map.jpg" alt="Map of my 2021 Swarm checkins" data-canonical-src="https://static.simonwillison.net/static/2021/dogsheep-swarm-map.jpg" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;h4&gt;Apple HealthKit&lt;/h4&gt;
&lt;p&gt;I don't yet have full automation for my Apple HealthKit data (collected by my Apple Watch) or my Apple Photos - both require me to run scripts on my laptop to create the SQLite database file and then copy the result to the server via &lt;code&gt;scp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://datasette.io/tools/healthkit-to-sqlite"&gt;healthkit-to-sqlite&lt;/a&gt; runs against the &lt;code&gt;export.zip&lt;/code&gt; that is produced by the Apple Health app on the iPhone's export data button - for me that was a 158MB zip file which I AirDropped to my laptop and converted (after &lt;a href="https://github.com/dogsheep/healthkit-to-sqlite/issues/19"&gt;fixing a new bug&lt;/a&gt;) like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;healthkit-to-sqlite ~/Downloads/export.zip healthkit.db
Importing from HealthKit  [-----------------------------]    2%  00:02:25
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I uploaded the resulting 1.5GB &lt;code&gt;healthkit.db&lt;/code&gt; file and now I can do things like visualize my 2017 San Francisco Half Marathon run on a map:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2021/dogsheep-marathon.jpg" alt="A map of the half marathon I ran" data-canonical-src="https://static.simonwillison.net/static/2021/dogsheep-marathon.jpg" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;h4&gt;Apple Photos&lt;/h4&gt;
&lt;p&gt;For my photos I use &lt;a href="https://datasette.io/tools/dogsheep-photos"&gt;dogsheep-photos&lt;/a&gt;, which I described last year in &lt;a href="https://simonwillison.net/2020/May/21/dogsheep-photos/"&gt;Using SQL to find my best photo of a pelican according to Apple Photos&lt;/a&gt;. The short version: I run this script on my laptop:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Upload original photos to my S3 bucket
dogsheep-photos upload photos.db \
  ~/Pictures/Photos\ Library.photoslibrary/originals
dogsheep-photos apple-photos photos.db \
  --image-url-prefix "https://photos.simonwillison.net/i/" \
  --image-url-suffix "?w=600"
scp photos.db dogsheep:/home/dogsheep/photos.db
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;photos.db&lt;/code&gt; is only 171MB - it contains the metadata, including the machine learning labels, but not the photos themselves.&lt;/p&gt;
&lt;p&gt;And now I can run queries for things like photos of food I've taken in 2021:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2021/dogsheep-photos-of-food.jpg" alt="Some photos of food" data-canonical-src="https://static.simonwillison.net/static/2021/dogsheep-photos-of-food.jpg" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;h4&gt;Automation via cron&lt;/h4&gt;
&lt;p&gt;I'm still working through the last step, which involves setting up cron tasks to refresh my data periodically from various sources. My &lt;code&gt;crontab&lt;/code&gt; currently looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Twitter
1,11,21,31,41,51 * * * * /home/dogsheep/datasette-venv/bin/twitter-to-sqlite user-timeline /home/dogsheep/twitter.db -a /home/dogsheep/auth.json --since
4,14,24,34,44,54 * * * * run-one /home/dogsheep/datasette-venv/bin/twitter-to-sqlite mentions-timeline /home/dogsheep/twitter.db -a /home/dogsheep/auth.json --since
11 * * * * run-one /home/dogsheep/datasette-venv/bin/twitter-to-sqlite user-timeline /home/dogsheep/twitter.db cleopaws -a /home/dogsheep/auth.json --since
6,16,26,36,46,56 * * * * run-one /home/dogsheep/datasette-venv/bin/twitter-to-sqlite favorites /home/dogsheep/twitter.db -a /home/dogsheep/auth.json --stop_after=50

# Swarm
25 */2 * * * /home/dogsheep/datasette-venv/bin/swarm-to-sqlite /home/dogsheep/swarm.db --token=... --since=2w

# Hacker News data every six hours
35 0,6,12,18 * * * /home/dogsheep/datasette-venv/bin/hacker-news-to-sqlite user /home/dogsheep/hacker-news.db simonw

# Re-build dogsheep-beta search index once an hour
32 * * * * /home/dogsheep/datasette-venv/bin/dogsheep-beta index /home/dogsheep/beta.db /home/dogsheep/dogsheep-beta.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I'll be expanding this out as I configure more of the &lt;a href="https://dogsheep.github.io/"&gt;Dogsheep tools&lt;/a&gt; for my personal instance.&lt;/p&gt;
&lt;h4&gt;TIL this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/sqlite/build-specific-sqlite-pysqlite-macos"&gt;Building a specific version of SQLite with pysqlite on macOS/Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/sqlite/track-timestamped-changes-to-a-table"&gt;Track timestamped changes to a SQLite table using triggers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/observable-plot/histogram-with-tooltips"&gt;Histogram with tooltips in Observable Plot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Releases this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/dogsheep/healthkit-to-sqlite"&gt;healthkit-to-sqlite&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/dogsheep/healthkit-to-sqlite/releases/tag/1.0.1"&gt;1.0.1&lt;/a&gt; - (&lt;a href="https://github.com/dogsheep/healthkit-to-sqlite/releases"&gt;9 releases total&lt;/a&gt;) - 2021-08-20
&lt;br /&gt;Convert an Apple Healthkit export zip to a SQLite database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/dogsheep/twitter-to-sqlite"&gt;twitter-to-sqlite&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/dogsheep/twitter-to-sqlite/releases/tag/0.21.4"&gt;0.21.4&lt;/a&gt; - (&lt;a href="https://github.com/dogsheep/twitter-to-sqlite/releases"&gt;27 releases total&lt;/a&gt;) - 2021-08-20
&lt;br /&gt;Save data from Twitter to a SQLite database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-block-robots"&gt;datasette-block-robots&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette-block-robots/releases/tag/1.0"&gt;1.0&lt;/a&gt; - (&lt;a href="https://github.com/simonw/datasette-block-robots/releases"&gt;5 releases total&lt;/a&gt;) - 2021-08-19
&lt;br /&gt;Datasette plugin that blocks robots and crawlers using robots.txt&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/sqlite-utils"&gt;sqlite-utils&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/sqlite-utils/releases/tag/3.16"&gt;3.16&lt;/a&gt; - (&lt;a href="https://github.com/simonw/sqlite-utils/releases"&gt;85 releases total&lt;/a&gt;) - 2021-08-18
&lt;br /&gt;Python CLI utility and library for manipulating SQLite databases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-debug-asgi"&gt;datasette-debug-asgi&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette-debug-asgi/releases/tag/1.1"&gt;1.1&lt;/a&gt; - (&lt;a href="https://github.com/simonw/datasette-debug-asgi/releases"&gt;3 releases total&lt;/a&gt;) - 2021-08-17
&lt;br /&gt;Datasette plugin for dumping out the ASGI scope&lt;/li&gt;
&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/linux"&gt;linux&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dogsheep"&gt;dogsheep&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="linux"/><category term="nginx"/><category term="datasette"/><category term="dogsheep"/><category term="weeknotes"/></entry><entry><title>Client-Side Certificate Authentication with nginx</title><link href="https://simonwillison.net/2019/Oct/5/client-side-certificate-authentication-nginx/#atom-tag" rel="alternate"/><published>2019-10-05T17:26:35+00:00</published><updated>2019-10-05T17:26:35+00:00</updated><id>https://simonwillison.net/2019/Oct/5/client-side-certificate-authentication-nginx/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://fardog.io/blog/2017/12/30/client-side-certificate-authentication-with-nginx/"&gt;Client-Side Certificate Authentication with nginx&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I’m intrigued by client-side browser certificates, which allow you to lock down a website such that only browsers with a specific certificate installed can access them. They work on both laptops and mobile phones. I followed the steps in this tutorial and managed to get an nginx instance running which only allows connections from my personal laptop and iPhone.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/certificates"&gt;certificates&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dogsheep"&gt;dogsheep&lt;/a&gt;&lt;/p&gt;



</summary><category term="certificates"/><category term="nginx"/><category term="security"/><category term="dogsheep"/></entry><entry><title>NGINX: Authentication Based on Subrequest Result</title><link href="https://simonwillison.net/2019/Oct/4/nginx-authentication-based-subrequest-result/#atom-tag" rel="alternate"/><published>2019-10-04T15:36:33+00:00</published><updated>2019-10-04T15:36:33+00:00</updated><id>https://simonwillison.net/2019/Oct/4/nginx-authentication-based-subrequest-result/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-subrequest-authentication/"&gt;NGINX: Authentication Based on Subrequest Result&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
TIL about this neat feature of NGINX: you can use the auth_request directive to cause NGINX to make an HTTP subrequest to a separate authentication server for each incoming HTTP request. The authentication server can see the cookies on the incoming request and tell NGINX if it should fulfill the parent request (via a 2xx status code) or if it should be denied (by returning a 401 or 403). This means you can run NGINX as an authenticating proxy in front of any HTTP application and roll your own custom authentication code as a simple webhook-recieving endpoint.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://github.com/simonw/datasette-auth-github/issues/45"&gt;Ishan Anand&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webhooks"&gt;webhooks&lt;/a&gt;&lt;/p&gt;



</summary><category term="authentication"/><category term="nginx"/><category term="webhooks"/></entry><entry><title>Mozilla Telemetry: In-depth Data Pipeline</title><link href="https://simonwillison.net/2018/Apr/12/in-depth-data-pipeline-detail/#atom-tag" rel="alternate"/><published>2018-04-12T15:44:42+00:00</published><updated>2018-04-12T15:44:42+00:00</updated><id>https://simonwillison.net/2018/Apr/12/in-depth-data-pipeline-detail/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://docs.telemetry.mozilla.org/concepts/pipeline/data_pipeline_detail.html#a-detailed-look-at-the-data-platform"&gt;Mozilla Telemetry: In-depth Data Pipeline&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Detailed behind-the-scenes look at an extremely sophisticated big data telemetry processing system built using open source tools. Some of this is unsurprising (S3 for storage, Spark and Kafka for streams) but the details are fascinating. They use a custom nginx module for the ingestion endpoint and have a “tee” server written in Lua and OpenResty which lets them route some traffic to alternative backend.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/reid_write/status/984412694336933889"&gt;@reid_write&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/analytics"&gt;analytics&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/lua"&gt;lua&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mozilla"&gt;mozilla&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/big-data"&gt;big-data&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/kafka"&gt;kafka&lt;/a&gt;&lt;/p&gt;



</summary><category term="analytics"/><category term="lua"/><category term="mozilla"/><category term="nginx"/><category term="big-data"/><category term="kafka"/></entry><entry><title>Andrew Godwin's www-router Docker container</title><link href="https://simonwillison.net/2018/Feb/21/www-router/#atom-tag" rel="alternate"/><published>2018-02-21T05:04:02+00:00</published><updated>2018-02-21T05:04:02+00:00</updated><id>https://simonwillison.net/2018/Feb/21/www-router/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/andrewgodwin/dockerfiles/tree/master/www-router"&gt;Andrew Godwin&amp;#x27;s www-router Docker container&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Really clever Docker trick: a container that runs Nginx  and uses it to route traffic to other containers based on the hostname—but the hostnames to be routed are configured using environment variables which the run-nginx.py CMD script uses to dynamically construct an nginx config when the container starts.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/andrewgodwin/status/966173557121101824"&gt;@andrewgodwin on Twitter&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/andrew-godwin"&gt;andrew-godwin&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/docker"&gt;docker&lt;/a&gt;&lt;/p&gt;



</summary><category term="andrew-godwin"/><category term="nginx"/><category term="docker"/></entry><entry><title>nginx proxy-cache-lock</title><link href="https://simonwillison.net/2017/Nov/14/proxy-cache-lock/#atom-tag" rel="alternate"/><published>2017-11-14T21:53:07+00:00</published><updated>2017-11-14T21:53:07+00:00</updated><id>https://simonwillison.net/2017/Nov/14/proxy-cache-lock/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_lock"&gt;nginx proxy-cache-lock&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Crucially important feature hidden away in the nginx documentation: proxy_cache_lock enables request coalescing, or dog-pile protection: it means that if a hundred simultaneous requests all suffer the same cache miss, only one request is made to the backend and the answer is then sent back to all hundred requests at once. I’ve leaned heavily on this feature in Varnish for years—useful to know that nginx has the same capability.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://blog.discordapp.com/how-discord-resizes-150-million-images-every-day-with-go-and-c-c9e98731c65d"&gt;How Discord Resizes 150 Million Images Every Day with Go and C++&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/caching"&gt;caching&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dogpile"&gt;dogpile&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/varnish"&gt;varnish&lt;/a&gt;&lt;/p&gt;



</summary><category term="caching"/><category term="dogpile"/><category term="nginx"/><category term="varnish"/></entry><entry><title>Running gunicorn behind nginx on Heroku for buffering and logging</title><link href="https://simonwillison.net/2017/Oct/2/nginx-heroku/#atom-tag" rel="alternate"/><published>2017-10-02T01:57:20+00:00</published><updated>2017-10-02T01:57:20+00:00</updated><id>https://simonwillison.net/2017/Oct/2/nginx-heroku/#atom-tag</id><summary type="html">
    &lt;p&gt;Heroku's default setup for Django uses the &lt;a href="http://gunicorn.org/"&gt;gunicorn&lt;/a&gt; application server. Each
Heroku dyno can only run a limited number of gunicorn workers, which means a
limited number of requests can be served in parallel (around 4 per dyno is a
good rule of thumb).&lt;/p&gt;

&lt;p&gt;Where things get nasty is when you have devices on slow connections - like
mobile phones. Heroku's router buffers headers but it does not buffer response
bodies, so a slow device could hold up a gunicorn worker for several seconds.
Too many slow devices at once and the site will become unavailable to other
users.&lt;/p&gt;

&lt;p&gt;This issue is explained and discussed here: &lt;a href="http://blog.etianen.com/blog/2014/01/19/gunicorn-heroku-django/"&gt;Don't use Gunicorn to host your Django sites on Heroku &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That article recommends using waitress as an alternative to gunicorn, but in
the comments at the bottom of the article people suggest using a Heroku
&lt;a href="https://github.com/beanieboi/nginx-buildpack"&gt;nginx-buildpack&lt;/a&gt; as an alternative.&lt;/p&gt;

&lt;p&gt;Here is a slightly out-of-date tutorial on getting this all set up: &lt;a href="https://koed00.github.io/Heroku_setups/"&gt;https://koed00.github.io/Heroku_setups/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I used the following commands to set up the buildpacks:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;heroku stack:set cedar-14
heroku buildpacks:clear
heroku buildpacks:add https://github.com/beanieboi/nginx-buildpack.git
heroku buildpacks:add https://github.com/heroku/heroku-buildpack-python.git
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Unfortunately the nginx buildpack is not yet compatible with the new &lt;samp&gt;heroku-16&lt;/samp&gt;
stack, so until the nginx buildpack has been updated it's necessary to run the
application on the older &lt;samp&gt;cedar-14&lt;/samp&gt; stack. See this discussion for details: &lt;a href="https://github.com/ryandotsmith/nginx-buildpack/issues/68"&gt;ryandotsmith/nginx-buildpack#68&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding nginx in this way also gives us the opportunity to fix another
limitation of Heroku: its default logging configuration. By default, log lines produced by Heroku (visible using &lt;samp&gt;heroku logs --tail&lt;/samp&gt; or with a logging addon such as &lt;a href="https://elements.heroku.com/addons/papertrail"&gt;Papertrail&lt;/a&gt;) look like
this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    Oct 01 18:01:06 simonwillisonblog heroku/router: at=info
        method=GET path="/2017/Oct/1/ship/" host=simonwillison.net
        request_id=bb22f67e-6924-4e81-b6ad-74d1f465cda7
        fwd="2001:8003:74c5:8b00:79e4:80ed:fa85:7b37,108.162.249.198"
        dyno=web.1 connect=0ms service=338ms status=200 bytes=4523 protocol=http
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notably missing here is both the user-agent string and the referrer header
sent by the browser! If you're a fan of tailing log files these omissions are pretty
disappointing.&lt;/p&gt;

&lt;p&gt;The nginx buildback I'm using loads a default configuration file at
&lt;samp&gt;config/nginx.conf.erb&lt;/samp&gt;. By including &lt;a href="https://github.com/simonw/simonwillisonblog/blob/ad874a2bf9ebfeffcb0a1a7f8594ad9735fcfc01/config/nginx.conf.erb"&gt;my own copy of this file&lt;/a&gt; I can override
the original and define my own custom log format.&lt;/p&gt;

&lt;p&gt;Having applied this change, the new log lines look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    2017-10-02T01:44:38.762845+00:00 app[web.1]:
        measure#nginx.service=0.133 request="GET / HTTP/1.1" status_code=200
        request_id=8b6402de-d072-42c4-9854-0f71697b30e5 remote_addr="10.16.227.159"
        forwarded_for="199.188.193.220" forwarded_proto="http" via="1.1 vegur"
        body_bytes_sent=12666 referer="-" user_agent="Mozilla/5.0 (Macintosh;
        Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko)
        Chrome/61.0.3163.100 Safari/537.36"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;This blog entry started life as &lt;a href="https://github.com/simonw/simonwillisonblog/commit/23615a4822ab463c611a3e6a1f4d6cb4dcfc5e7b"&gt;a commit message&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/logging"&gt;logging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/user-agents"&gt;user-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/heroku"&gt;heroku&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gunicorn"&gt;gunicorn&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="django"/><category term="logging"/><category term="nginx"/><category term="user-agents"/><category term="heroku"/><category term="gunicorn"/></entry><entry><title>What exactly the error meaning client closed prematurely connection while sending to client from upstream in nginx?</title><link href="https://simonwillison.net/2013/Oct/27/what-exactly-the-error/#atom-tag" rel="alternate"/><published>2013-10-27T13:33:00+00:00</published><updated>2013-10-27T13:33:00+00:00</updated><id>https://simonwillison.net/2013/Oct/27/what-exactly-the-error/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-exactly-the-error-meaning-client-closed-prematurely-connection-while-sending-to-client-from-upstream-in-nginx/answer/Simon-Willison"&gt;What exactly the error meaning client closed prematurely connection while sending to client from upstream in nginx?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I think it means that the connection to the user's browser was lost before the request had been fully transferred - for example due to the user hitting the stop button in their browser or switching off their wifi connection.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="nginx"/><category term="quora"/></entry><entry><title>How can I determine which web server a particular website is using (Apache, IIS, Nginx, etc)?</title><link href="https://simonwillison.net/2011/Jan/5/how-can-i-determine/#atom-tag" rel="alternate"/><published>2011-01-05T13:37:00+00:00</published><updated>2011-01-05T13:37:00+00:00</updated><id>https://simonwillison.net/2011/Jan/5/how-can-i-determine/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/How-can-I-determine-which-web-server-a-particular-website-is-using-Apache-IIS-Nginx-etc/answer/Simon-Willison"&gt;How can I determine which web server a particular website is using (Apache, IIS, Nginx, etc)?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you're on Linux or OS X, use curl with the -I option (to make a HEAD request and see the HTTP headers):&lt;/p&gt;

&lt;p&gt;$ curl -I &lt;span&gt;&lt;a href="http://www.op3intl.com"&gt;www.op3intl.com&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;HTTP/1.1 200 OK&lt;br /&gt;Date: Thu, 06 Jan 2011 03:31:28 GMT&lt;br /&gt;Server: Microsoft-IIS/6.0&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/apache"&gt;apache&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/iis"&gt;iis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/servers"&gt;servers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webservers"&gt;webservers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="apache"/><category term="iis"/><category term="nginx"/><category term="servers"/><category term="web-development"/><category term="webservers"/><category term="quora"/></entry><entry><title>What are the advantages of running Apache behind nginx as opposed to just Apache by itself?</title><link href="https://simonwillison.net/2010/Oct/12/what-are-the-advantages/#atom-tag" rel="alternate"/><published>2010-10-12T14:09:00+00:00</published><updated>2010-10-12T14:09:00+00:00</updated><id>https://simonwillison.net/2010/Oct/12/what-are-the-advantages/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-are-the-advantages-of-running-Apache-behind-nginx-as-opposed-to-just-Apache-by-itself/answer/Simon-Willison"&gt;What are the advantages of running Apache behind nginx as opposed to just Apache by itself?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I do this for all of my Django stuff - I have Django running on modwsgi on a stripped down Apache (almost no configuration except for the modwsgi stuff), then I put an nginx on port 80 which serves the static files directly and proxies dynamic requests back to Apache.&lt;/p&gt;

&lt;p&gt;There are a few advantages to this. Firstly, I use non-threaded Apache so each Apache worker process tends to be pretty big (having a full Python interpreter + Django application running in it). It's a horrible waste of resources for one of those fat processes to sit around serving up a static file instead of serving a dynamic request. Secondly, nginx deals with slow connections for me - if someone on a modem (or 3G phone) hits my site I don't tie up an Apache process for several seconds to respond to their request. Instead, nginx sucks the response in from Apache in a few ms and then spools it out to the slow client at whatever rate they can handle. Since nginx is evented it can handle hundreds or even thousands of slow clients at once using very little memory.&lt;/p&gt;

&lt;p&gt;Finally, if I'm running an nginx proxy at the "front" of my stack it's trivial for me to start load balancing to more than one Apache servers if I need to.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/apache"&gt;apache&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webservers"&gt;webservers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="apache"/><category term="nginx"/><category term="webservers"/><category term="quora"/></entry><entry><title>Multi-node: Concurrent NodeJS HTTP Server</title><link href="https://simonwillison.net/2010/Jul/15/multinode/#atom-tag" rel="alternate"/><published>2010-07-15T08:22:00+00:00</published><updated>2010-07-15T08:22:00+00:00</updated><id>https://simonwillison.net/2010/Jul/15/multinode/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.sitepen.com/blog/2010/07/14/multi-node-concurrent-nodejs-http-server/"&gt;Multi-node: Concurrent NodeJS HTTP Server&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Kris Zyp’s library for spawning multiple Node child processes (one per core is suggested) for concurrent request handling, taking advantage of Node’s child_process module. This alleviates the need to run multiple Node instances behind an nginx load balancer in order to take advantage of multiple cores.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/kriszyp"&gt;kriszyp&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nodejs"&gt;nodejs&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/recovered"&gt;recovered&lt;/a&gt;&lt;/p&gt;



</summary><category term="javascript"/><category term="kriszyp"/><category term="nginx"/><category term="nodejs"/><category term="recovered"/></entry><entry><title>Introduction to nginx.conf scripting</title><link href="https://simonwillison.net/2010/Apr/21/nginx/#atom-tag" rel="alternate"/><published>2010-04-21T23:40:46+00:00</published><updated>2010-04-21T23:40:46+00:00</updated><id>https://simonwillison.net/2010/Apr/21/nginx/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://agentzh.org/misc/slides/nginx-conf-scripting/nginx-conf-scripting.html#1"&gt;Introduction to nginx.conf scripting&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Slideshow—hit left arrow to navigate through the slides. The nginx community is officially nuts. Starts out with a simple “Hello world” using the echo module, then rapidly descends down the rabbit hole in to array operations, sub-requests, memcached connection pooling and eventually non-blocking Drizzle SQL execution against a sharded cluster—all implemented in the nginx.conf configuration file.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/drizzle"&gt;drizzle&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/http"&gt;http&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/memcached"&gt;memcached&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;&lt;/p&gt;



</summary><category term="drizzle"/><category term="http"/><category term="memcached"/><category term="nginx"/></entry><entry><title>jacobian's django-deployment-workshop</title><link href="https://simonwillison.net/2010/Feb/19/jacobians/#atom-tag" rel="alternate"/><published>2010-02-19T14:28:35+00:00</published><updated>2010-02-19T14:28:35+00:00</updated><id>https://simonwillison.net/2010/Feb/19/jacobians/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://github.com/jacobian/django-deployment-workshop"&gt;jacobian&amp;#x27;s django-deployment-workshop&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Notes and resources from Jacob’s 3 hour Django deployment workshop at PyCon, including example configuration files for Apache2 + mod_wsgi, nginx, PostgreSQL and pgpool.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/apache"&gt;apache&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/deployment"&gt;deployment&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jacob-kaplan-moss"&gt;jacob-kaplan-moss&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/modwsgi"&gt;modwsgi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pgpool"&gt;pgpool&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pycon"&gt;pycon&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sysadmin"&gt;sysadmin&lt;/a&gt;&lt;/p&gt;



</summary><category term="apache"/><category term="deployment"/><category term="django"/><category term="jacob-kaplan-moss"/><category term="modwsgi"/><category term="nginx"/><category term="pgpool"/><category term="postgresql"/><category term="pycon"/><category term="python"/><category term="sysadmin"/></entry><entry><title>Round-robin Django setup with nginx</title><link href="https://simonwillison.net/2009/Dec/21/nginx/#atom-tag" rel="alternate"/><published>2009-12-21T15:43:42+00:00</published><updated>2009-12-21T15:43:42+00:00</updated><id>https://simonwillison.net/2009/Dec/21/nginx/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://smallpy.posterous.com/round-robin-django-setup-with-nginx"&gt;Round-robin Django setup with nginx&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
An nginx trick I didn’t know: a low proxy_connect_timeout value (e.g. 2 seconds) combined with the proxy_next_upstream setting means that if one of your backends breaks a user won’t even see an error, they’ll just have a short delay before getting a response from a working server.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/load-balancing"&gt;load-balancing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ops"&gt;ops&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sysadmin"&gt;sysadmin&lt;/a&gt;&lt;/p&gt;



</summary><category term="django"/><category term="load-balancing"/><category term="nginx"/><category term="ops"/><category term="sysadmin"/></entry><entry><title>Simple CouchDB multi-master clustering via Nginx</title><link href="https://simonwillison.net/2009/Nov/19/cluster/#atom-tag" rel="alternate"/><published>2009-11-19T16:37:36+00:00</published><updated>2009-11-19T16:37:36+00:00</updated><id>https://simonwillison.net/2009/Nov/19/cluster/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://ephemera.karmi.cz/post/247255194/simple-couchdb-multi-master-clustering-via-nginx"&gt;Simple CouchDB multi-master clustering via Nginx&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
An impressive combination. CouchDB can be easily set up in a multi-master configuration, where writes to one master are replicated to the other and vice versa. This makes setting up a reliable CouchDB cluster is as simple as putting two such servers behind a single nginx proxy.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cluster"&gt;cluster&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/couchdb"&gt;couchdb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/load-balancing"&gt;load-balancing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/multimaster"&gt;multimaster&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/replication"&gt;replication&lt;/a&gt;&lt;/p&gt;



</summary><category term="cluster"/><category term="couchdb"/><category term="load-balancing"/><category term="multimaster"/><category term="nginx"/><category term="replication"/></entry><entry><title>How We Made GitHub Fast</title><link href="https://simonwillison.net/2009/Oct/21/github/#atom-tag" rel="alternate"/><published>2009-10-21T21:14:38+00:00</published><updated>2009-10-21T21:14:38+00:00</updated><id>https://simonwillison.net/2009/Oct/21/github/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://github.com/blog/530-how-we-made-github-fast"&gt;How We Made GitHub Fast&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Detailed overview of the new GitHub architecture. It’s a lot more complicated than I would have expected—lots of moving parts are involved in ensuring they can scale horizontally when they need to. Interesting components include nginx, Unicorn, Rails, DRBD, HAProxy, Redis, Erlang, memcached, SSH, git and a bunch of interesting new open source projects produced by the GitHub team such as BERT/Ernie and ProxyMachine.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/drbd"&gt;drbd&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/erlang"&gt;erlang&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ernie"&gt;ernie&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/git"&gt;git&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/haproxy"&gt;haproxy&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/memcached"&gt;memcached&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/proxymachine"&gt;proxymachine&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rails"&gt;rails&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/redis"&gt;redis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/replication"&gt;replication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ruby"&gt;ruby&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ssh"&gt;ssh&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/unicorn"&gt;unicorn&lt;/a&gt;&lt;/p&gt;



</summary><category term="drbd"/><category term="erlang"/><category term="ernie"/><category term="git"/><category term="github"/><category term="haproxy"/><category term="memcached"/><category term="nginx"/><category term="proxymachine"/><category term="rails"/><category term="redis"/><category term="replication"/><category term="ruby"/><category term="scaling"/><category term="ssh"/><category term="unicorn"/></entry><entry><title>nginx_http_push_module</title><link href="https://simonwillison.net/2009/Oct/17/push/#atom-tag" rel="alternate"/><published>2009-10-17T16:48:31+00:00</published><updated>2009-10-17T16:48:31+00:00</updated><id>https://simonwillison.net/2009/Oct/17/push/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://github.com/slact/nginx_http_push_module"&gt;nginx_http_push_module&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
More clever design with webhooks—here’s an nginx module that provides a comet endpoint URL which will hang until a back end process POSTs to another URL on the same server. This makes it much easier to build asynchronous comet apps using regular synchronous web frameworks such as Django, PHP and Rails.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/comet"&gt;comet&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rails"&gt;rails&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webhooks"&gt;webhooks&lt;/a&gt;&lt;/p&gt;



</summary><category term="comet"/><category term="django"/><category term="nginx"/><category term="php"/><category term="rails"/><category term="webhooks"/></entry><entry><title>Ravelry</title><link href="https://simonwillison.net/2009/Sep/3/ravelry/#atom-tag" rel="alternate"/><published>2009-09-03T18:50:20+00:00</published><updated>2009-09-03T18:50:20+00:00</updated><id>https://simonwillison.net/2009/Sep/3/ravelry/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.tbray.org/ongoing/When/200x/2009/09/02/Ravelry"&gt;Ravelry&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Tim Bray interviews Casey Forbes, the single engineer behind Ravelry, the knitting community that serves 10 million Rails requests a day using just seven physical servers, MySQL, Sphinx, memcached, nginx, haproxy, passenger and Tokyo Cabinet.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/caseyforbes"&gt;caseyforbes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/haproxy"&gt;haproxy&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/memcached"&gt;memcached&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/passenger"&gt;passenger&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rails"&gt;rails&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ravelry"&gt;ravelry&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sphinx-search"&gt;sphinx-search&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tim-bray"&gt;tim-bray&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tokyocabinet"&gt;tokyocabinet&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tokyotyrant"&gt;tokyotyrant&lt;/a&gt;&lt;/p&gt;



</summary><category term="caseyforbes"/><category term="haproxy"/><category term="memcached"/><category term="mysql"/><category term="nginx"/><category term="passenger"/><category term="rails"/><category term="ravelry"/><category term="scaling"/><category term="sphinx-search"/><category term="tim-bray"/><category term="tokyocabinet"/><category term="tokyotyrant"/></entry><entry><title>Phusion Passenger for nginx</title><link href="https://simonwillison.net/2009/Apr/20/passenger/#atom-tag" rel="alternate"/><published>2009-04-20T04:53:55+00:00</published><updated>2009-04-20T04:53:55+00:00</updated><id>https://simonwillison.net/2009/Apr/20/passenger/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://blog.phusion.nl/2009/04/16/phusions-one-year-anniversary-gift-phusion-passenger-220/"&gt;Phusion Passenger for nginx&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Passenger (aka mod_rails / mod_rack) enables easy deployment of Rails and Ruby apps under Apache... and the latest version adds support for nginx as well. It works as an HTTP proxy and process manager, spawning worker processes and forwarding HTTP requests to them via a request queue. It can also handle Python WSGI applications—anyone tried it out for that yet?


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/apache"&gt;apache&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/deployment"&gt;deployment&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/passenger"&gt;passenger&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rails"&gt;rails&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ruby"&gt;ruby&lt;/a&gt;&lt;/p&gt;



</summary><category term="apache"/><category term="deployment"/><category term="nginx"/><category term="passenger"/><category term="python"/><category term="rails"/><category term="ruby"/></entry><entry><title>Paul Buchheit: Make your site faster and cheaper to operate in one easy step</title><link href="https://simonwillison.net/2009/Apr/17/paul/#atom-tag" rel="alternate"/><published>2009-04-17T17:19:44+00:00</published><updated>2009-04-17T17:19:44+00:00</updated><id>https://simonwillison.net/2009/Apr/17/paul/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://paulbuchheit.blogspot.com/2009/04/make-your-site-faster-and-cheaper-to.html"&gt;Paul Buchheit: Make your site faster and cheaper to operate in one easy step&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Paul promotes gzip encoding using nginx as a proxy, and mentions that FriendFeed use a “custom, epoll-based python server” as their application server. Does that mean that they’re serving their real-time comet feeds directly from Python?


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/comet"&gt;comet&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/epoll"&gt;epoll&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/friendfeed"&gt;friendfeed&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gzip"&gt;gzip&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/paul-buchheit"&gt;paul-buchheit&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;&lt;/p&gt;



</summary><category term="comet"/><category term="epoll"/><category term="friendfeed"/><category term="gzip"/><category term="nginx"/><category term="paul-buchheit"/><category term="python"/></entry><entry><title>How to use Django with Apache and mod_wsgi</title><link href="https://simonwillison.net/2009/Apr/1/modwsgi/#atom-tag" rel="alternate"/><published>2009-04-01T00:24:04+00:00</published><updated>2009-04-01T00:24:04+00:00</updated><id>https://simonwillison.net/2009/Apr/1/modwsgi/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://docs.djangoproject.com/en/dev/howto/deployment/modwsgi/"&gt;How to use Django with Apache and mod_wsgi&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
My favourite deployment option is now included in the official Django docs, thanks to Alex Gaynor. I tend to run a stripped down Apache with mod_wsgi behind an nginx proxy, and have nginx serve static files directly. This avoids the need for a completely separate media server (although a separate media domain is still a good idea for better client-side performance).


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/alex-gaynor"&gt;alex-gaynor&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/deployment"&gt;deployment&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/modwsgi"&gt;modwsgi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/proxies"&gt;proxies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/wsgi"&gt;wsgi&lt;/a&gt;&lt;/p&gt;



</summary><category term="alex-gaynor"/><category term="deployment"/><category term="django"/><category term="modwsgi"/><category term="nginx"/><category term="proxies"/><category term="python"/><category term="wsgi"/></entry><entry><title>Future roadmap for mod_wsgi</title><link href="https://simonwillison.net/2009/Mar/19/modwsgi/#atom-tag" rel="alternate"/><published>2009-03-19T17:27:01+00:00</published><updated>2009-03-19T17:27:01+00:00</updated><id>https://simonwillison.net/2009/Mar/19/modwsgi/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://blog.dscpl.com.au/2009/03/future-roadmap-for-modwsgi.html"&gt;Future roadmap for mod_wsgi&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
mod_wsgi 3.0 isn’t too far off, and will include Python 3.0 support, WSGI application preloading and internal web server redirection (similar to nginx X-Accel-Redirect). Version 4.0 plans a major architectural change that will allow multiple versions of Python to be run from the same Apache.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/apache"&gt;apache&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/graham-dumpleton"&gt;graham-dumpleton&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/modwsgi"&gt;modwsgi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/wsgi"&gt;wsgi&lt;/a&gt;&lt;/p&gt;



</summary><category term="apache"/><category term="graham-dumpleton"/><category term="modwsgi"/><category term="nginx"/><category term="python"/><category term="wsgi"/></entry><entry><title>The Django and Ubuntu Intrepid Almanac</title><link href="https://simonwillison.net/2009/Feb/14/comprehensive/#atom-tag" rel="alternate"/><published>2009-02-14T15:42:58+00:00</published><updated>2009-02-14T15:42:58+00:00</updated><id>https://simonwillison.net/2009/Feb/14/comprehensive/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://lethain.com/entry/2009/feb/13/the-django-and-ubuntu-intrepid-almanac/"&gt;The Django and Ubuntu Intrepid Almanac&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Will Larson’s impressively comprehensive guide to configuring and securing an Ubuntu VPS from scratch to run Django, using PostgreSQL and Apache/mod_wsgi behind nginx.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/apache"&gt;apache&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/modwsgi"&gt;modwsgi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sysadmin"&gt;sysadmin&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ubuntu"&gt;ubuntu&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vps"&gt;vps&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/will-larson"&gt;will-larson&lt;/a&gt;&lt;/p&gt;



</summary><category term="apache"/><category term="django"/><category term="modwsgi"/><category term="nginx"/><category term="postgresql"/><category term="sysadmin"/><category term="ubuntu"/><category term="vps"/><category term="will-larson"/></entry><entry><title>Minimal nginx conf to split get/post requests</title><link href="https://simonwillison.net/2008/Oct/14/django/#atom-tag" rel="alternate"/><published>2008-10-14T16:33:38+00:00</published><updated>2008-10-14T16:33:38+00:00</updated><id>https://simonwillison.net/2008/Oct/14/django/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.djangosnippets.org/snippets/1141/"&gt;Minimal nginx conf to split get/post requests&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Interesting idea for master-slave replication balancing where GET v.s. POST is load-balanced by nginx, presumably to different backend servers that are configured to talk to either a slave or a master. This won’t deal very will with replication lag though—you really want a user’s session to be bound to the master server for the next few GET requests after data is modified to ensure they see the effects of their updates. UPDATE: Amit fixed my complaint with a neat hack based around a cookie with a max age of 10 seconds.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/load-balancing"&gt;load-balancing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/masterslave"&gt;masterslave&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/replication"&gt;replication&lt;/a&gt;&lt;/p&gt;



</summary><category term="load-balancing"/><category term="masterslave"/><category term="mysql"/><category term="nginx"/><category term="replication"/></entry><entry><title>ncache</title><link href="https://simonwillison.net/2008/Jun/18/ncache/#atom-tag" rel="alternate"/><published>2008-06-18T20:09:48+00:00</published><updated>2008-06-18T20:09:48+00:00</updated><id>https://simonwillison.net/2008/Jun/18/ncache/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://code.google.com/p/ncache/"&gt;ncache&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
A squid-style caching system built on top of nginx. Supports the HTTP PURGE method for cache invalidation.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cache"&gt;cache&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/http"&gt;http&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ncache"&gt;ncache&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/purge"&gt;purge&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/squid"&gt;squid&lt;/a&gt;&lt;/p&gt;



</summary><category term="cache"/><category term="http"/><category term="ncache"/><category term="nginx"/><category term="purge"/><category term="squid"/></entry><entry><title>Load Balancer Update</title><link href="https://simonwillison.net/2008/May/1/load/#atom-tag" rel="alternate"/><published>2008-05-01T10:06:13+00:00</published><updated>2008-05-01T10:06:13+00:00</updated><id>https://simonwillison.net/2008/May/1/load/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://barry.wordpress.com/2008/04/28/load-balancer-update/"&gt;Load Balancer Update&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
WordPress.com has switched from Pound to nginx for load balancing, resulting in a significant drop in CPU usage. I’ve been using nginx on my little VPS for over a year now with no complaints, nice to know it scales up as well as down.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/load-balancing"&gt;load-balancing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pound"&gt;pound&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/wordpress"&gt;wordpress&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/wordpresscom"&gt;wordpresscom&lt;/a&gt;&lt;/p&gt;



</summary><category term="load-balancing"/><category term="nginx"/><category term="pound"/><category term="wordpress"/><category term="wordpresscom"/></entry><entry><title>Nginx and Memcached, a 400% boost!</title><link href="https://simonwillison.net/2008/Feb/11/nginx/#atom-tag" rel="alternate"/><published>2008-02-11T22:05:11+00:00</published><updated>2008-02-11T22:05:11+00:00</updated><id>https://simonwillison.net/2008/Feb/11/nginx/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.igvita.com/2008/02/11/nginx-and-memcached-a-400-boost/"&gt;Nginx and Memcached, a 400% boost!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Ilya Grigorik wrote up my current favourite nginx trick—you set nginx to check memcached for a cache entry matching the current URL on every hit, then invalidate your cache by pushing a new cache record straight in to memcached from your application server.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/caching"&gt;caching&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ilyagrigorik"&gt;ilyagrigorik&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/memcached"&gt;memcached&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/performance"&gt;performance&lt;/a&gt;&lt;/p&gt;



</summary><category term="caching"/><category term="ilyagrigorik"/><category term="memcached"/><category term="nginx"/><category term="performance"/></entry><entry><title>NginxMemcachedModule</title><link href="https://simonwillison.net/2007/Dec/15/nginxmemcachedmodule/#atom-tag" rel="alternate"/><published>2007-12-15T01:59:23+00:00</published><updated>2007-12-15T01:59:23+00:00</updated><id>https://simonwillison.net/2007/Dec/15/nginxmemcachedmodule/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://wiki.codemongers.com/NginxMemcachedModule"&gt;NginxMemcachedModule&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
nginx can be set up to directly serve a URL from memcache if the corresponding cache key is set, and fall back to a backend application server otherwise. Application servers can then write directly to memcache when content needs to be cached or goes stale.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/caching"&gt;caching&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/memcache"&gt;memcache&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/memcached"&gt;memcached&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;&lt;/p&gt;



</summary><category term="caching"/><category term="memcache"/><category term="memcached"/><category term="nginx"/><category term="scaling"/></entry><entry><title>A Fair Proxy Balancer for Nginx and Mongrel</title><link href="https://simonwillison.net/2007/Dec/9/fair/#atom-tag" rel="alternate"/><published>2007-12-09T14:57:44+00:00</published><updated>2007-12-09T14:57:44+00:00</updated><id>https://simonwillison.net/2007/Dec/9/fair/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://brainspl.at/articles/2007/11/09/a-fair-proxy-balancer-for-nginx-and-mongrel"&gt;A Fair Proxy Balancer for Nginx and Mongrel&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
nginx uses round robin for proxying by default; this extension module ensures requests are queued up and sent through to backend mongrel servers that aren’t currently busy. I don’t see any reason this wouldn’t work with servers other than mongrel.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/fair"&gt;fair&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/load-balancing"&gt;load-balancing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mongrel"&gt;mongrel&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/proxies"&gt;proxies&lt;/a&gt;&lt;/p&gt;



</summary><category term="fair"/><category term="load-balancing"/><category term="mongrel"/><category term="nginx"/><category term="proxies"/></entry></feed>