1

We are using ETag and If-None-Match headers to improve the performances of our Single Page Application on the initial load. I have noticed that Internet Explorer do not react the same way than Firefox and Chrome when a redirection (303 or 302) is send by the server.

Observation:

When a Server sends for a given page a 302 Code to indicate that the page should be reloaded (the location header points to the same page). Then IE use its cached version (it sends the If-None-Match and get the 304 answer) where Firefox and Chrome consider that the page should be reloaded.

With a simple server that sends always the same page (with a weak ETag value) except each fith response where it send a redirection (302 code) to the same page.

Firefox:

Firefox - Network tab

Chrome:

Chrome - Network tab

Internet Explorer 11:

IE 11 - Network tab

Question:

Is this an intended behavior? Is there a possibility to force IE to reload the page?

Wrong solution:

Based on the answers I have read on StackOverflow:

I have tried to send Expires and Cache-Control when I send my 302 redirect request, but then IE does:

  • User Request (F5), response is a 302 code with headers Expires: -1 and Cache-Control: must-revalidate, private
  • Request to follow the redirection, request with header If-None-Match, response is a 304 code.
  • User Request (F5), without header If-None-Match, response is a 200 code.

IE 11 - Network tab

Code for the experiment:

I have adapted the Jetty Hello-World example:

package org.eclipse.jetty.embedded;

import java.io.IOException;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;

public class JettyHelloWorld extends AbstractHandler {
    public static final String IF_NONE_MATCH = "If-None-Match"; //$NON-NLS-1$

    long time = new Date().getTime();
    int counter = 0;

    @Override
    public void handle(String target,
            Request baseRequest,
            HttpServletRequest request,
            HttpServletResponse response) throws IOException,
            ServletException {

        String etag = createEtag();
        String ifNoneMatch = request.getHeader(IF_NONE_MATCH);
        boolean clientSentEtag = (ifNoneMatch != null);

        counter = counter + 1;
        System.out.print("[" + (counter % 5) + "]");
        if (counter % 5 == 0) {
            System.out.println("SEND 302 for " + request.getPathInfo());
            // response.setHeader("Expires", "-1");
            // response.setHeader("Cache-Control", "must-revalidate, private");
            response.setHeader("Location", "/");
            response.setStatus(HttpServletResponse.SC_FOUND);
            // response.sendRedirect("/");
            baseRequest.setHandled(true);
        } else if (clientSentEtag && notModified(ifNoneMatch, etag)) {
            // Check If-None-Match (Etag)
            System.out.println("SEND 304 for " + request.getPathInfo());
            response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            baseRequest.setHandled(true);
        } else {
            System.out.println("SEND 200 for " + request.getPathInfo());
            response200(baseRequest, response);
        }

    }

    private void response200(Request baseRequest, HttpServletResponse response) throws IOException {
        // Declare response encoding and types
        response.setContentType("text/html; charset=utf-8");

        response.addHeader("ETag", createEtag());

        // Declare response status code
        response.setStatus(HttpServletResponse.SC_OK);

        // Write back response
        response.getWriter()
                .println("<h1>Hello World:" + time + "</h1>");

        // Inform jetty that this request has now been handled
        baseRequest.setHandled(true);
    }

    private String createEtag() {
        return "W/\"" + time + "\"";
    }

    private boolean notModified(String ifNoneMatch, String etag) {
        return (ifNoneMatch != null && etag != null && ifNoneMatch.indexOf(etag) != -1);
    }

    public static void main(String[] args) throws Exception {
        Server server = new Server(8080);
        server.setHandler(new JettyHelloWorld());

        server.start();
        server.join();
    }
}

Dependency:

<dependency>
    <groupId>org.eclipse.jetty.aggregate</groupId>
    <artifactId>jetty-all</artifactId>
    <version>9.3.6.v20151106</version>
    <type>pom</type>
</dependency>

Background:

This example is really minimalistic. In our productive environment, the 302 Redirect is produced by the login module. The problem is that sometimes, IE is just displaying a white window instead of the page content.

Jmini
  • 9,189
  • 2
  • 55
  • 77
  • I am curious if you ever found a workaround for this. – Eduardo La Hoz Miranda Feb 27 '18 at 15:38
  • No. Our solution was to inactivate caching for this page in order to have the same behaviour in IE as in other browsers. Nobody else seems to have this issue with IE. – Jmini Feb 28 '18 at 11:53
  • @Jmini this does not seem to work for chrome either. on redirect the browser is not sending the etag. – mjs Oct 02 '21 at 15:12
  • @Jmini, I know this question is old and IE has gone out of support in the meantime, but I wonder why you need(ed) the 302 redirect at all, when the redirect points to the same page? Why is it not possible to send the HTTP 200 response directly? Probably I'm missing something and this is a stupid question, sorry in advance… – siegi Jun 26 '22 at 20:14

0 Answers0