Understanding Nginx Browser Caching Mechanisms
Browser Caching Principles
HTTP protocol defines caching mechanisms including Expires and Cache-Control headers.
Request flow without browser cache:
Client → Server → Response (200 OK)
Request flow with browser cache:
Client → Server → Check Validation → Response (304 Not Modified)
Cache Validation and Expiration Mechanism
Step 1: Client-Side Cache Validation
The client validates local cache using Expires and Cache-Control (max-age) headers to determine if the cached resource has expired.
Expireswas introduced in HTTP/1.0Cache-Control(max-age) was introduced in HTTP/1.1
Step 2: ETag Validation
If the cache hasn't expired based on the first step, the client sends an ETag header to verify if the server-side cache is still valid. If valid, the server returns a 304 response without transferring the file.
About ETag:
The ETag value is a string identifier. When a file is updated within one second, Last-Modified cannot detect this change, but ETag can idetnify it.
Step 3: Last-Modified Validation
If ETag validation passes, the client uses the Last-Modified header to verify server-side cache freshness. If valid, a 304 response is returned; otherwise, the server sends the updated file.
About Last-Modified:
The Last-Modified value represents a specific timestamp: YYYY-MM-DD HH:MM:SS.
When a cached file on the server has been modified at a newer time than the client's cached version, the timestamps won't match. The server then returns the updated file, leveraging the Last-Modified header in the request for validation.
Cache flow from client to server:
Request with If-Modified-Since / If-None-Match → Server Check → 304 or Updated File
304 Status Code: Indicates the client has sent a conditional GET request, but the file hasn't changed since the last request.
Browser Caching Demo
Nginx adds Cache-Control and Expires headers to responses using the expires directive.
expires Directive Syntax
Syntax: expires [modified] time;
expires epoch | max | off;
Default: expires off;
Context: http, server, location, if in location
Demo 1: Without expires Configuration
Nginx location block:
location ~ .*\.(htm|html)$ {
root /var/www/html;
}
Creating test content:
Create index.html in /var/www/html:
<h1>Hello World</h1>
Testing the request:
http://www.example.com/index.html
First request: Returns 200 OK with full content.
Subsequent requests: Returns 304 Not Modified.
The client sends these headers for server-side validation:
If-Modified-Since:If-None-Match:
Demo 2: With expires Configuration
Nginx location block:
location ~ .*\.(htm|html)$ {
expires 24h;
root /var/www/html;
}
First request: Response headers include:
Cache-Control: max-age=86400Expires: [timestamp 24 hours from now]
Second request: Response still returns 304, but the client checks:
If-Modified-Since:If-None-Match:
Regardless of whether expires is configured, subsequent requests will always include If-Modified-Since and If-None-Match headers for cache validation.
The Cache-Control header value remains consistent across all requests within the cache validity period, while Expires provides a specific expiration timestamp for older HTTP/1.0 compatibility.