On a small VPS, the safest way to reduce application load is to cache only public, repeatable responses and to prevent a burst of identical requests from reaching the origin at once. Use proxy_cache when Nginx proxies an HTTP application server; use fastcgi_cache for PHP or another FastCGI application. In both cases, define a disk cache, set explicit expiry and bypass rules, enable cache locking for busy pages, and measure the result instead of assuming a speed-up.
Antihost offers Linux VPS plans with full root access, unlimited traffic on a 1 Gbps port and a 14-day free trial.
What Nginx caching actually does
When caching is enabled, Nginx stores an upstream response on disk and can serve a later request for the same cache key without contacting the application. The first request normally obtains the response from the origin and writes it to the cache; subsequent matching requests are cache hits until the object expires, is purged, or becomes unusable.
As an Amazon Associate I earn from qualifying purchases.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →The cache is not a replacement for adequate memory, a correctly tuned database, or an application that fails under load. It is a way to avoid repeating identical work. A public article, product information page, or API response that is the same for many visitors is a good candidate. A dashboard containing a user’s name, a shopping cart, or a page that depends on a session is not safe to cache without a carefully designed private-cache strategy.
Choose the correct cache module
| Question | Use proxy caching | Use FastCGI caching |
|---|---|---|
| What protocol does Nginx use upstream? | HTTP, configured with proxy_pass |
FastCGI, commonly configured with fastcgi_pass |
| Typical application | Node.js, Python, Ruby, Go, or another HTTP server | PHP-FPM or another FastCGI service |
| Main directives | proxy_cache_path, proxy_cache, proxy_cache_valid |
fastcgi_cache_path, fastcgi_cache, fastcgi_cache_valid |
| Cache key | Usually scheme, host, and request URI; define it explicitly when needed | Usually scheme, request method, host, and URI; define it explicitly when needed |
| Most important safety check | Do not cache authorization, session, preview, cart, or other personalized requests | Apply the same checks, plus verify that PHP output cannot contain user-specific data |
| Origin connection tuning | Use upstream keepalive with the correct HTTP settings | Use an upstream keepalive design and set fastcgi_keep_conn on |
Do not enable both caches for the same response path merely because both directives exist. Identify the upstream protocol first, then configure the matching module.
#1 Best Overall
Inspect your current server before changing it
Find the active Nginx configuration and identify the server block handling the site. Look for proxy_pass, fastcgi_pass, PHP location blocks, and any existing cache directives. Validate a change before reloading:
sudo nginx -t
sudo systemctl reload nginx
The test must report a successful configuration. A reload applies a valid configuration without dropping established client connections; if the test fails, correct the syntax and do not reload the broken file.
Record a baseline for at least one normal traffic period: upstream response time, CPU use, memory pressure, disk I/O, request rate, HTTP errors, and the application’s own logs. Official Nginx documentation does not promise a universal percentage improvement. The result depends on the hit rate, response size, storage speed, application work, and VPS limits.
Build a disk cache in the http context
The cache path and shared-memory zone belong in the top-level http {} context, not inside an individual location. The shared zone stores keys and metadata; response bodies are stored under the cache directory.
http {
proxy_cache_path /var/cache/nginx/public
levels=1:2
keys_zone=public_cache:20m
max_size=1g
inactive=30m
use_temp_path=off;
fastcgi_cache_path /var/cache/nginx/fastcgi
levels=1:2
keys_zone=fastcgi_cache_zone:20m
max_size=1g
inactive=30m
use_temp_path=off;
# server blocks follow here
}
levels: creates subdirectories so a very large number of files is not placed in one directory.keys_zone: allocates shared memory for cache keys and metadata. The name must match the cache directive used later.max_size: sets a disk limit for that cache. Choose a value that leaves room for the operating system, logs, uploads, and the application.inactive: allows objects that have not been requested for the stated interval to be removed, even if their nominal validity period has not elapsed.use_temp_path=off: keeps temporary files in the cache filesystem. When temporary and cache files share a filesystem, Nginx can generally rename the completed file instead of copying it, reducing disk work.
Create the directories with ownership that allows the Nginx worker to write them. Check available space and inode usage before selecting max_size. A cache that fills the VPS disk can cause failures far beyond a slow page.
Configure safe proxy caching for an HTTP upstream
This example caches only GET and HEAD requests for a public location. It bypasses caching when common authentication or personalization signals are present and exposes a header that makes testing easier.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Rank #2
server {
listen 80;
server_name example.test;
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache public_cache;
proxy_cache_methods GET HEAD;
proxy_cache_key "$scheme|$request_method|$host|$request_uri";
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
proxy_cache_bypass $http_authorization $cookie_session $arg_preview $arg_cart;
proxy_no_cache $http_authorization $cookie_session $arg_preview $arg_cart;
proxy_cache_lock on;
proxy_cache_lock_timeout 10s;
proxy_cache_lock_age 5s;
add_header X-Cache-Status $upstream_cache_status always;
}
}
Use a separate location or an explicit bypass for login, account, checkout, preview, administrative, and API endpoints that return private data. The example cookie and query names are only examples; replace them with the names your application actually uses. A bypass prevents Nginx from reading a cached object for a request, while proxy_no_cache prevents the response from being stored. Using both avoids accidentally serving or saving a personalized response.
Keep the cache key stable and intentional. Including the full request URI preserves meaningful query parameters, but it can also create a different object for every tracking parameter. If parameters such as analytics tags do not change the page, normalize them before caching rather than allowing unlimited one-off keys. Never remove a parameter that changes the response.
Configure FastCGI caching for PHP or another FastCGI app
FastCGI has its own cache module and directives. The location must pass the standard FastCGI parameters while applying the same public-content safety rules.
location ~ .php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_cache fastcgi_cache_zone;
fastcgi_cache_methods GET HEAD;
fastcgi_cache_key "$scheme|$request_method|$host|$request_uri";
fastcgi_cache_valid 200 5m;
fastcgi_cache_valid 404 30s;
fastcgi_cache_bypass $http_authorization $cookie_session $arg_preview $arg_cart;
fastcgi_no_cache $http_authorization $cookie_session $arg_preview $arg_cart;
fastcgi_cache_lock on;
fastcgi_cache_lock_timeout 10s;
fastcgi_cache_lock_age 5s;
fastcgi_keep_conn on;
add_header X-Cache-Status $upstream_cache_status always;
}
Adapt the socket path and parameter include to the operating system and PHP-FPM version installed on the VPS. Do not cache every PHP request by default: PHP can generate both public pages and highly personal responses. Many sites also exclude POST requests, logged-in users, preview modes, and endpoints that set or read a session cookie.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteResponses containing Set-Cookie are not cached by default. Treat that behavior as a safety feature. Do not disable it globally just to raise the hit rate; first prove that the response is public and that the cookie cannot identify or modify a user’s session.
Set expiry, revalidation, and stale-response behavior deliberately
proxy_cache_valid and fastcgi_cache_valid provide defaults by status code. A response can also influence its lifetime through X-Accel-Expires, Expires, and Cache-Control headers. Application headers may therefore shorten, extend, or otherwise control the effective freshness according to Nginx’s cache rules. Inspect actual response headers rather than assuming the directive alone determines the result.
Short lifetimes reduce the chance of stale content but increase origin requests. Longer lifetimes improve hit rate but require a reliable invalidation plan. For a small VPS, start with a short period on frequently edited pages and a longer period on genuinely stable public assets or documents. Do not claim that a cache is fresh merely because the file remains on disk; freshness and physical presence are different states.
For an origin that is briefly unavailable, stale serving can preserve availability, but it can also display outdated information. Configure stale controls only for responses where that trade-off is acceptable, and document which status codes or upstream failures permit stale content. Revalidation can reduce full refetches when the application supplies validators, but it still requires a request to the origin.
Prevent a cache stampede with locking
When a popular object expires, many clients can miss at the same time. Without protection, every miss may run the application query or PHP script. proxy_cache_lock on and fastcgi_cache_lock on allow one request to populate the missing item while other requests wait for that fill, reducing a thundering herd against a small upstream.
Set a lock timeout that is longer than a normal origin response but short enough that clients do not wait indefinitely. The lock age determines when Nginx may allow another request to begin filling if the first fill has taken unusually long. Test these values under realistic concurrency; an excessively short timeout can recreate the burst you intended to prevent, while an excessively long timeout can increase client latency during an origin problem.
Keep upstream connections efficient without creating a false cap
Upstream keepalive avoids repeatedly performing connection setup. For an HTTP upstream, use HTTP/1.1 and remove an unnecessary Connection: close header in the upstream configuration. For FastCGI, fastcgi_keep_conn on is required for persistent connections to function.
Keepalive is not a total connection limit. Nginx’s upstream documentation specifically warns that the keepalive directive does not limit the total number of connections a worker can open to an upstream server. Size worker, upstream, application-process, and database limits together. If the backend cannot accept the possible concurrency, use appropriate queueing and rate controls rather than assuming keepalive alone protects it.
Rank #4
Observe established and active connections, upstream response time, and application worker saturation after enabling keepalive. A setting that helps a network-bound backend can hurt a memory-constrained one if it leaves too many idle connections or competes with application processes for RAM.
Compress proxied text responses selectively
By default, Nginx does not compress responses to proxied requests. Gzip can reduce bandwidth for text, JSON, JavaScript, CSS, XML, and similar content, but compression consumes CPU. Enable it where the VPS has spare CPU and where the response is large enough to justify the work.
http {
gzip on;
gzip_proxied any;
gzip_min_length 1024;
gzip_comp_level 4;
gzip_types
text/plain
text/css
text/xml
application/json
application/javascript
application/xml
application/rss+xml;
}
The one-kilobyte threshold is an example, not a universal optimum. Measure CPU and transfer volume, and avoid compressing already-compressed formats such as JPEG, PNG, WebP, ZIP, or most video. Make sure the response’s Vary behavior remains correct when clients differ in compression support. If CPU is the bottleneck, compression can increase load even while it saves bandwidth.
Test hits, misses, bypasses, and private pages
- Request a known public URL once and record the response time and
X-Cache-Status. - Request it again with the same host, scheme, path, and relevant headers. A successful second request should normally report a hit after the first response has been stored.
- Send an authorization header or the application’s session cookie. The response should bypass the public cache and must not replace the cached anonymous object.
- Test preview, cart, search, language, device, and tracking parameters that your application treats specially. Confirm that parameters affecting output remain in the key and harmless parameters do not create uncontrolled fragmentation.
- Request a page that sets
Set-Cookieand verify that it is not stored unless you have deliberately designed a safe public response. - Wait for expiry or remove only the intended cache files, then confirm that one origin request fills the object while concurrent requests do not create a burst.
Log or expose cache status only in a way that does not disclose sensitive headers to end users. A response header is convenient during setup, but production observability is better handled through access logs and restricted diagnostic endpoints.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Operate the cache safely on a small VPS
Plan restart windows
Loading an entire cache at startup can consume enough resources to slow the VPS during the first minutes after a restart. Large caches therefore have an operational cost even when they improve steady-state hit rate. Restart during a low-traffic period, monitor CPU, memory, disk I/O, and upstream latency, and choose cache size with restart behavior in mind.
Watch the metrics that explain the result
- Cache status: count hits, misses, bypasses, expired objects, and stale responses.
- Origin time: compare upstream response time on misses with total client time on hits.
- Resource pressure: track CPU, memory, swap, disk latency, disk space, and inode use.
- Correctness: sample anonymous, authenticated, preview, and session-bearing requests for leakage or stale data.
- Errors: watch 4xx and 5xx rates at both Nginx and the application.
A high hit rate is not automatically a success if pages are wrong, memory is exhausted, or a slow disk makes cache reads expensive. Conversely, a modest hit rate may still help when misses avoid an expensive database operation.
Invalidate with a plan
Expiry alone may be sufficient for news or documentation with an acceptable delay. For rapidly changing public data, provide a controlled purge or versioned URL strategy. Never delete the entire cache reflexively during an incident without checking disk space, origin capacity, and the likely refill burst; a cold cache can send every visitor back to the application at once.
Free tools Windows power users keep installed
One-click scans. No signup required.
Common failure modes and fixes
| Symptom | Likely cause | What to check |
|---|---|---|
| Every request is a miss | The key changes, caching is bypassed, or the response is not cacheable | Inspect the key inputs, cookies, authorization, status code, and response headers |
| Logged-in users see anonymous content | Personalized requests are entering the public cache | Add both bypass and no-cache rules for the real session and authorization signals; purge affected objects |
| Origin receives a burst at expiry | Cache locking is disabled or its timeout is too short | Enable the matching lock directive and test lock timing under concurrency |
| Cache files fill the disk | max_size is too high, another directory shares the volume, or inactive cleanup is unsuitable |
Check free space and inodes, reduce limits, and keep logs and uploads protected |
| FastCGI keepalive has no effect | fastcgi_keep_conn on is missing or backend limits are reached |
Verify the directive, socket behavior, PHP-FPM capacity, and connection metrics |
| Compression raises CPU usage | Small responses or already-compressed files are being processed | Raise gzip_min_length, narrow MIME types, or disable gzip where CPU is scarce |
| Restart temporarily slows the site | The cache is large and startup loading competes for resources | Schedule restarts, reduce cache size, and monitor the warm-up period |
A practical rollout sequence
- Classify each route as static, public dynamic, authenticated, administrative, or transactional.
- Choose
proxy_cachefor an HTTP upstream orfastcgi_cachefor FastCGI. - Create a cache path and shared zone in
http {}with conservative disk and memory limits. - Start with one public GET/HEAD endpoint and an explicit cache key.
- Add bypass and no-cache rules for authorization, sessions, preview, cart, and every other personalization signal.
- Set status-specific validity periods and confirm how application cache headers affect them.
- Enable cache locking for endpoints that are expensive to generate or likely to receive concurrent misses.
- Validate the configuration, reload, and test hit, miss, bypass, cookie, query-string, and expiry behavior.
- Only after correctness is established, tune upstream keepalive and selective gzip.
- Compare resource and application metrics with the baseline, then expand the cache one route at a time.
This sequence keeps the highest-risk decision—whether a response is safe to share—ahead of performance tuning. Caching fewer pages correctly is preferable to caching the whole site and discovering a privacy or freshness problem later.
Frequently Asked Questions
Why does my cache status show BYPASS for every request?
Check each bypass variable, especially cookie names and authorization headers, then inspect the response for Set-Cookie or cache-control directives. A rule intended for logged-in users may be matching an anonymous cookie that the application sets for everyone.
Should I cache a page that has a small user-specific widget?
Keep the document uncached unless the widget is loaded separately. Split the public page from the personalized fragment with a client-side request or an explicitly private caching design; do not share a response containing one user’s data.
Is deleting the cache a safe way to fix stale content?
It removes stale objects but creates a cold-cache refill burst. Purge only the affected keys when possible, check origin capacity first, and use versioned URLs or controlled invalidation for recurring updates.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

