Understanding Browser Cache Control with Spring MVC and Nginx

This article explains how browsers cache resources using HTTP headers such as Last-Modified, Expires, Cache-Control and ETag, demonstrates Java Spring MVC code to set these headers, and shows how to configure Nginx for both static and proxy caching to reduce backend load and improve performance.

Qunar Tech Salon
Qunar Tech Salon
Qunar Tech Salon
Understanding Browser Cache Control with Spring MVC and Nginx

The article explains how browsers cache resources using HTTP response headers such as Last-Modified , Expires , Cache-Control and ETag , and how a Java Spring MVC application can control these headers to enable efficient client‑side caching.

It provides a complete Spring MVC controller example that sets Last-Modified, Date, Expires and Cache-Control headers based on a supplied modification timestamp, returning 304 Not Modified when the client’s If-Modified-Since matches.

@RequestMapping("/cache")
public ResponseEntity<String> cache(HttpServletRequest request,
      @RequestParam("millis") long lastModifiedMillis,
      @RequestHeader(value = "If-Modified-Since", required = false) Date ifModifiedSince) {
    long now = System.currentTimeMillis();
    long maxAge = 20;
    if (ifModifiedSince != null && ifModifiedSince.getTime() == lastModifiedMillis) {
        return new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
    }
    DateFormat gmtDateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
    String body = "<a href=''>点击访问当前链接</a>";
    MultiValueMap<String, String> headers = new HttpHeaders();
    headers.add("Last-Modified", gmtDateFormat.format(new Date(lastModifiedMillis)));
    headers.add("Date", gmtDateFormat.format(new Date(now)));
    headers.add("Expires", gmtDateFormat.format(new Date(now + maxAge)));
    headers.add("Cache-Control", "max-age=" + maxAge);
    return new ResponseEntity<>(body, headers, HttpStatus.OK);
}

Another example shows how to generate a weak ETag and return 304 Not Modified when the client’s If-None-Match matches, illustrating the difference between weak and strong validators.

@RequestMapping("/cache/etag")
public ResponseEntity<String> cache(HttpServletRequest request,
      HttpServletResponse response,
      @RequestHeader(value = "If-None-Match", required = false) String ifNoneMatch) {
    long now = System.currentTimeMillis();
    long maxAge = 10;
    String body = "<a href=''>点击访问当前链接</a>";
    String etag = "W/\"" + md5(body) + "\"";
    if (StringUtils.equals(ifNoneMatch, etag)) {
        return new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
    }
    DateFormat gmtDateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
    MultiValueMap<String, String> headers = new HttpHeaders();
    headers.add("ETag", etag);
    headers.add("Date", gmtDateFormat.format(new Date(now)));
    headers.add("Cache-Control", "max-age=" + maxAge);
    return new ResponseEntity<>(body, headers, HttpStatus.OK);
}

The article then discusses Nginx cache configuration, including the expires, etag and if-modified-since directives for static resources, and demonstrates how to set up a reverse‑proxy cache with proxy_cache, proxy_cache_valid and related directives to reduce backend load.

location /img {
    alias /export/img/;
    expires 1d;
}

It also covers advanced Nginx proxy cache options such as proxy_cache_path, proxy_cache_key, proxy_cache_use_stale, proxy_cache_revalidate, and cache‑status headers (HIT, MISS, EXPIRED, UPDATING, STALE, REVALIDATED, BYPASS), providing practical tips for tuning cache size, inactivity, lock mechanisms and eviction policies.

proxy_cache_path /export/cache/proxy_cache levels=1:2 keys_zone=cache:512m inactive=5m max_size=8g use_temp_path=off;
proxy_cache_key $scheme$proxy_host$request_uri;
proxy_cache_valid 200 5s;
add_header cache-status $upstream_cache_status;

Finally, the article shares operational best practices for caching HTTP 200 responses, setting appropriate cache durations for static assets and dynamic data, handling cache invalidation in large‑scale e‑commerce scenarios, and using tools such as ngx_cache_purge for manual cache clearing.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

cachinghttp-headersBrowser Cache
Qunar Tech Salon
Written by

Qunar Tech Salon

Qunar Tech Salon is a learning and exchange platform for Qunar engineers and industry peers. We share cutting-edge technology trends and topics, providing a free platform for mid-to-senior technical professionals to exchange and learn.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.