Jump to content
MediaWiki

Manual:Nginx caching

From mediawiki.org

This page describes an alternative to Manual:Varnish caching for sites using Nginx. It supports multiple wikis on one server, and serving cached mobile pages from the same domain using Extension:MobileFrontend. Cache purging is supported, so anonymous visitors always get up to date content.

Note: The strategy used forces the Accept-Language header to be en-US, so the UI language for anonymous visitors will always be English. You can change this to any other language, if your wiki is primarily targeted at another demographic. Caching different versions of pages depending on the Accept-Language header sent by the visitor's browser is not supported.

This article assumes some basic familiarity with the configuration of Nginx, PHP, and MediaWiki.

Prelude

[edit ]

When serving MediaWiki from Nginx using PHP-FPM, it's possible to use the caching capabilities of the Nginx FastCGI module to cache responses generated by MediaWiki's PHP code. This allows Nginx to serve anonymous visitors directly from memory, without needing to ask the PHP-FPM process to execute any code at all.

Visitors who are logged in, or have made edits (and thus receive a session token), should not be served from this cache, since MediaWiki's PHP code needs to customize the page for them, such as to display their user name, load user CSS/JS, display talk page notifications, and so on.

This caching strategy should be used in addition to, and not as a replacement for, the parser cache and other internal caching mechanisms of MediaWiki. Even if you use this strategy, you should still also use the various internal caching mechanisms supported by MediaWiki, such as with APCu, memcached, and so on.

However, a traditional "front-end HTTP cache" such as Varnish is essentially rendered obsolete when using this strategy. That being said, Varnish may be better suited for setups in which there are multiple back-end servers for load-balancing purposes. This strategy instead assumes a single Nginx server hosting one or more wikis.

On-disk or in-memory

[edit ]

Although the Nginx FastCGI cache uses the filesystem, one can arrange for it to reside in a RAM filesystem such as /dev/shm to make sure it's entirely in-memory. If a persistent cache is desired, an SSD should also work well, especially since modern operating systems cache accessed files in memory anyway.

Packages needed

[edit ]

The strategy detailed below uses the libnginx-mod-http-cache-purge package, available on Debian 13, to purge pages from the cache when they're edited, so anonymous visitors get up to date content.

For Nginx itself, the author of this article uses the nginx-full package, although the smaller packages such as nginx-core or even nginx-light may work as well. (FastCGI seems available in all.)

Installing nginx-extras will pull in the required libnginx-mod-http-cache-purge package automatically as a dependency.

The cache purge package may be available on older Debian versions, or other operating systems, as well. However, make sure it's based on the newer fork found here rather than the very old original which doesn't seem to support wildcard keys for purging (this will be explained later).

Note: As of October 2025, a bug in the aforementioned purge module leads to memory leaks when using wildcard purging. A patch to solve this has been submitted but waiting approval, and may require some time until propagated into various GNU/Linux package repositories. You can compile and deploy the fix manually by following these instructions if you're comfortable doing so.

If no purge module is available, you can still use the caching strategy on this page, but will have to live with the fact that anonymous visitors may get outdated wiki content until the cached version of a page expires on its own.

Nginx configuration

[edit ]

Debian ships an /etc/nginx/nginx.conf file that automatically includes further configuration files from /etc/nginx/conf.d, and virtual-host configuration files from /etc/nginx/sites-enabled (a symlink farm to /etc/nginx/sites-available). It also ships /etc/nginx/fastcgi.conf which contains a standard set of FastCGI configuration parameters.

If you don't already have such a core structure under /etc/nginx, please make sure to create one first. The rest of this section assumes such a base setup and won't teach you how to create it. If in doubt, copy what Debian 13 does, because this article is written

The configuration is split up as follows:

  • /etc/nginx/conf.d/php-fpm.conf: Generic PHP-FPM settings.
  • /etc/nginx/conf.d/mediawiki-fcgi.conf: The "MediaWiki FastCGI / Cached" core setup.
  • /etc/nginx/snippets/run-php-direct: Snippet to include to run PHP code bypassing the cache.
  • /etc/nginx/snippets/run-php-mwfc: Snippet to include to run PHP code using the "MediaWiki FastCGI / Cached" setup.
  • /etc/nginx/sites-enabled/mwfc-purge: The purge request handler, so to speak.
  • /etc/nginx/sites-enabled/wiki1: Virtual-host configuration for wiki 1. (example)
  • /etc/nginx/sites-enabled/wiki2: Virtual-host configuration for wiki 2. (example)

You can have multiple wikis under sites-enabled and the caching configuration will ensure that their pages aren't confused for each other even if they happen to have identical titles.

php-fpm.conf

[edit ]

This file is very simple and merely defines the location of the PHP-FPM socket as an "upstream" in Nginx terms.

upstreamphp_fpm{
serverunix:/run/php/php8.4-fpm.sock;
}

mediawiki-fcgi.conf

[edit ]

This is the most complex file, and contains the majority of the caching setup.

Please tweak values in this file to suit your needs. This one uses up to 16 GB of RAM!

This is also where you define the location of the cache; this example uses /dev/shm/ngx-mwcache which means it resides on an in-memory filesystem whose contents will be gone after a reboot.

For details on how the cache purging works, read the comments in this file under NEW PURGE STRATEGY. It explains how we arrange for the mobile version of a page to be purged as well when a purge for the URL is requested. It also makes sure that responses for HTTP requests other than GET, such as HEAD, are also purged.

#
# MediaWiki FastCGI with Response Caching (Setup)
#
# This file is meant to be included from within an http { } block.
# See 'snippets/run-php-mwfc' for usage within location { } blocks.
#
# Variables defined with map are prefixed to prevent clashes with other
# parts of your Nginx configuration in case you have a complex config.
#
# The prefix "mwfc" stands for "MediaWiki FastCGI / Cached".
#
# The cache is held in RAM and may use up to 16 GB of memory.
# The key zone may use an additional 250 MB of memory.
fastcgi_cache_path
/dev/shm/ngx-mwcache
levels=1:2
keys_zone=MW:250m
max_size=16000m
inactive=2d;
# Check whether user is visiting from mobile.
# Use a simple 1/0 to be used as part of the cache key.
map$http_user_agent$mwfc_mobile{
default0;
# Regexes based on:
# https://gerrit.wikimedia.org/r/plugins/gitiles/operations/puppet/+/refs/heads/production/modules/varnish/templates/text-frontend.inc.vcl.erb
"~(SMART-TV.*SamsungBrowser)"0;
"~*^(lge?|sie|nec|sgh|pg)-"1;
"~*(mobi|240x240|240x320|320x320|alcatel|android|audiovox|bada|benq|blackberry|cdm-|compal-|docomo|ericsson|hiptop|htc[-_]|huawei|ipod|kddi-|kindle|meego|midp|mitsu|mmp\/|mot-|motor|ngm_|nintendo|opera.m|palm|panasonic|philips|phone|playstation|portalmmm|sagem-|samsung|sanyo|sec-|semc-browser|sendo|sharp|silk|softbank|symbian|teleca|up.browser|vodafone|webos)"
1;
}
### OLD PURGE STRATEGY ###
#
# PURGE request purges cached responses for GET requests.
# Responses for other requests, like HEAD, can't be purged.
# We also can't purge mobile from desktop, and vice versa.
#
#map $request_method $mwfc_req_key {
# default $request_method;
# PURGE GET;
#}
#
#fastcgi_cache_key "$host$request_uri$mwfc_req_key$mwfc_mobile";
#
### END OLD PURGE STRATEGY ###
### NEW PURGE STRATEGY ###
#
# Use a wildcard to purge responses for all HTTP methods, mobile and desktop.
#
# However, we don't want a PURGE for the URI /foo to also purge /foo/bar.
# Worst case would be "/" purging "/*" i.e. all URIs.
#
# For this reason, we terminate the URI with a pipe. This should be safe,
# because the pipe should be percent-encoded in the received URI.
#
map$request_method$mwfc_cache_key{
default"$host$request_uri|$request_method$mwfc_mobile";
PURGE"$host$request_uri|*";
}
fastcgi_cache_key"$mwfc_cache_key";
### END NEW PURGE STRATEGY ###
# We will imitate Apache's AMF headers, which use true/false.
map$mwfc_mobile$mwfc_mobile_amf{
0false;
1true;
}
# This is the simplest way to make Extension:MobileFrontend understand
# that we do our own UA detection so it doesn't attempt to do its own.
fastcgi_paramAMF_DEVICE_IS_MOBILE$mwfc_mobile_amf;
# MediaWiki doesn't set Cache-Control for some Special:MyXyz pages.
# https://phabricator.wikimedia.org/T272431
map$request_uri$mwfc_bypass_uri{
default0;
"~*/Special:My"1;
"~*/Special:AllMy"1;
}
# Does the URI represent a resource that varies on cookies?
# To be safe, assume all URIs do, except those specified.
map$request_uri$mwfc_bypass_cookie_uri{
default1;
"~^/w/(load|opensearch_desc|thumb)\.php"0;
"~^/php/"0;
}
# Do we actually have a cookie that causes response variance?
map$http_cookie$mwfc_bypass_cookie_value{
default0;
"~([sS]ession|Token)="1;
"~mf_useformat"1;
}
# Perform an AND on the previous two variables.
map$mwfc_bypass_cookie_uri$mwfc_bypass_cookie_value$mwfc_bypass_cookie{
default0;
"11"1;
}
log_formatmwfc_stats
"$upstream_cache_status$status$request_method$host$request_uri";

run-php-direct

[edit ]

This is a snippet file to be included from location { } blocks when PHP code should be executed unconditionally, without consulting the cache or attempting to store the result.

include/etc/nginx/fastcgi.conf;
fastcgi_passphp_fpm;

run-php-mwfc

[edit ]

This snippet file is included from location { } blocks when you want PHP code execution with caching.

#
# MediaWiki FastCGI with Response Caching (Execution)
#
# This file is meant to be included from within location { } blocks,
# to actually pass a request to PHP-FPM.
#
# See conf.d/mediawiki-fcgi.conf for the setup this depends on.
#
include/etc/nginx/fastcgi.conf;
#access_log /var/log/nginx/mwcache-stats.log mwfc_stats gzip;
fastcgi_passphp_fpm;
fastcgi_cacheMW;
fastcgi_cache_valid1h;
fastcgi_cache_use_staleerrortimeouthttp_500http_503;
# Ignore Vary because we took care of everything ourselves.
fastcgi_ignore_headersVary;
# Anon visitors only get English.
fastcgi_paramHTTP_ACCEPT_LANGUAGEen-US;
# Variable $mwfc_bypass_cookie defined in MWFC setup file.
fastcgi_cache_bypass$mwfc_bypass_uri$mwfc_bypass_cookie;
# Note: This doesn't correspond to the upstream FastCGI docs!
#
# The functionality is provided by this package:
# libnginx-mod-http-cache-purge
#
# Whose documentation can be found here:
# https://github.com/nginx-modules/ngx_cache_purge
#
fastcgi_cache_purgePURGEfrom127.0.0.0/8;
add_headerX-Cache$upstream_cache_statusalways;

mwfc-purge

[edit ]

This virtual-host configuration file is just to handle the PURGE requests coming from localhost, sent by MediaWiki itself.

Use of TCP port 1080 seems to be hardcoded into MediaWiki, so let's hope you don't use it for something else already!

server{
listen1080default_server;
access_log/var/log/nginx/mwfc-purge-access.log;
error_log/var/log/nginx/mwfc-purge-error.log;
location/{
if($request_method!="PURGE"){
return405;
}
includesnippets/run-php-mwfc;
}
}

wiki1 / wiki2 / etc.

[edit ]

Here's an example wiki virtual-host configuration. This one is taken from bg3.wiki and simplified down a bit.

Note that the Cache-Control headers set in some cases have nothing to do with our PHP caching strategy. These just tell the visitor's browser that it can cache certain files, like images or CSS code files, for a certain duration.

# Easily toggle logging on/off here
map""$bg3log{
default0;
}
map$request_uri$bg3log_pagehits{
default0;
"~^/wiki/"1;# toggle page-hit logging
}
log_formatbg3wiki_pagehits
'$time_iso8601 $remote_addr $status '
'$request_uri $http_referer "$http_user_agent"';
server{
listen443ssl;
server_namebg3.wiki;
ssl_certificate/etc/letsencrypt/live/bg3.wiki/fullchain.pem;
ssl_certificate_key/etc/letsencrypt/live/bg3.wiki/privkey.pem;
access_log/var/log/nginx/bg3wiki-access.logcombinedif=$bg3log;
error_log/var/log/nginx/bg3wiki-error.log;
access_log/var/log/nginx/bg3wiki-pagehits.log
bg3wiki_pagehitsif=$bg3log_pagehits;
log_not_foundoff;
root/var/www/bg3;
# No cache for robots, sitemaps
location~^/(robots|sitemap){
}
# Static assets outside of MediaWiki
location~^/(favicon|static){
# Cache 30 days
add_headerCache-Control"max-age=2592000, public";
}
# Custom CSS and JS code
location~^/(css|js)/{
# Cache 5 min
add_headerCache-Control"max-age=300, public";
}
# Custom PHP scripts
location/php/{
includesnippets/run-php-mwfc;
}
# Root URL is main page
location=/{
fastcgi_indexw/index.php;
includesnippets/run-php-mwfc;
}
location=/wiki/Main_Page{
return301/;
}
# Location for wiki's entry points
location~^/w/(index|load|api|thumb|opensearch_desc|rest)\.php${
includesnippets/run-php-mwfc;
}
# Block Discord's broken page preview tool
location=/w/api.php{
if($http_user_agent~"Discordbot"){
return403;
}
includesnippets/run-php-mwfc;
}
# Images
location/w/images{
# Cache 24 h
add_headerCache-Control"max-age=86400, public";
}
location/w/images/deleted{
denyall;
}
# MediaWiki assets (usually images)
location~^/w/resources/(assets|lib|src){
# Cache 30 days
add_headerCache-Control"max-age=2592000, public";
}
# Assets, scripts and styles from skins and extensions
location~^/w/(skins|extensions)/.+\.(css|js|gif|ico|jpg|jpeg|png|svg|webp|wasm|ttf|woff|woff2)${
# Cache 30 days
add_headerCache-Control"max-age=2592000, public";
}
# Handling for Mediawiki REST API, see [[mw:API:REST_API]]
location/w/rest.php/{
try_files$uri$uri//w/rest.php?$query_string;
}
# Handling for the article path (pretty URLs)
location/wiki/{
rewrite^/wiki/(?<pagename>.*)$/w/index.php;
}
# Every other entry point will be disallowed.
# Add specific rules for other entry points/images as needed above this
location/{
return404;
}
}
# On port 80, handle LetsEncrypt requests, otherwise redirect to HTTPS.
server{
listen80;
server_namebg3.wikiwww.bg3.wiki;
access_log/var/log/nginx/bg3wiki-redirect.logcombinedif=$bg3log;
error_log/var/log/nginx/bg3wiki-error.log;
location/.well-known/acme-challenge{
root/var/www/html;
try_files$uri=404;
}
location/{
return301https://bg3.wiki$request_uri;
}
}
# Redirect erroneous www. subdomain requests to main domain.
server{
listen443ssl;
server_namewww.bg3.wiki;
ssl_certificate/etc/letsencrypt/live/bg3.wiki/fullchain.pem;
ssl_certificate_key/etc/letsencrypt/live/bg3.wiki/privkey.pem;
access_log/var/log/nginx/bg3wiki-redirect.logcombinedif=$bg3log;
error_log/var/log/nginx/bg3wiki-error.log;
return301https://bg3.wiki$request_uri;
}

MediaWiki configuration

[edit ]

Finally, to make sure MediaWiki's PHP code sets the correct response headers for Nginx to actually cache the responses, and to make MediaWiki sent PURGE requests for edited pages, we need to set some options in LocalSettings.php:

# Allow caching via reverse proxy; in our case the Nginx FastCGI cache.
$wgUseCdn = true;
$wgCdnMaxAge = 3600;
# Make MediaWiki send PURGE requests to Nginx
# Note that this implicitly uses port 1080
$wgCdnServers = [ '127.0.0.1' ];
$wgInternalServer = 'http://bg3.wiki';

See also

[edit ]

AltStyle によって変換されたページ (->オリジナル) /