CORS Request Preflight Scrutiny

Patient Tools

Read, save, and share this guide

Use these quick tools to make this medical article easier to read, print, save, or share with a family member.

Article Summary

CORS stands for Cross-Origin Resource Sharing. Is an feature offering the possbility to: A web application to expose resources to all or restricted domain, A web client to made AJAX request for resource on other domain than is source domain. This article will focus on HTTP Request Preflight feature proposed by CORS W3C specification and (mainly) how to setup a protection, on web application side, against CORS HTTP request that try...

Key Takeaways

  • This article explains Request preflight process overview in simple medical language.
  • This article explains Risk in simple medical language.
  • This article explains Countermeasure in simple medical language.
  • This article explains Informations links in simple medical language.
Educational health guideWritten for patient understanding and clinical awareness.
Reviewed content workflowUse writer and reviewer profiles for stronger trust.
Emergency safety firstUrgent warning signs are highlighted below.

Seek urgent medical care if you notice

These warning signs are general safety guidance. Local emergency numbers and clinical judgment should always come first.

  • Severe symptoms, breathing difficulty, fainting, confusion, or rapidly worsening illness.
  • New weakness, severe pain, high fever, or symptoms after a serious injury.
  • Any symptom that feels urgent, unusual, or unsafe for the patient.
1

Emergency now

Use emergency care for severe, sudden, rapidly worsening, or life-threatening symptoms.

2

See a doctor

Book a professional medical evaluation if symptoms persist, worsen, recur often, affect daily activities, or occur in a high-risk patient.

3

Learn safely

Use this article to understand possible causes, tests, treatment options, prevention, and questions to ask your clinician.

CORS stands for Cross-Origin Resource Sharing.

Is an feature offering the possbility to:

  • A web application to expose resources to all or restricted domain,
  • A web client to made AJAX request for resource on other domain than is source domain.

This article will focus on HTTP Request Preflight feature proposed by CORS W3C specification and (mainly) how to setup a protection, on web application side, against CORS HTTP request that try to bypass the preflight process.

Request preflight process overview

In order to not duplicate explanation, and because Mozilla wiki have a great introduction article about CORS, you can read a description of the process using link below:

Risk

Request preflight have to objective to ensure that HTTP request will not have a bad impact on data, this, using a first request in which browser describe the final HTTP request that will send later.

The main risk here (for web application), is that the request preflight process is entirely managed on client side (by the browser) and then anything warrant web application that the request preflight process will be always followed…

A user can create/send (using tools like Curl, ZAP,…) a final HTTP request without previously send the first request for preflight and then bypass request preflight process in order to act on data in a unsafe way.

Countermeasure

We must ensure the Request Preflight process compliance on server side.

To achieve it we will use JEE Web Filter that will check every CORS request using theses steps:

  • Step 1 : Determine the type of the incoming request,
  • Step 2 : Process request according to is type using temporary cache to keep state of preflighting step of the process.

Sample implementation: Filter class

```java
import java.io.IOException;
import java.util.Collections;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.PersistenceConfiguration;
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;

/**
 * Sample filter implementation to scrutiny that CORS "Request Preflight" process is followed by HTTP request concerned.<br/>
 *
 * This implementation has a dependency on EHCache API because it use Caching to store preflighted requests.
 *
 * Assume here that all CORS resources are grouped in context path "/cors/".
 *
 * @see "https://developer.mozilla.org/en-US/docs/HTTP_access_control#Simple_requests"
 */
@SuppressWarnings("static-method")
@WebFilter("/cors/*")
public class CORSRequestPreflightProcessScrutiny implements Filter {

    // Filter configuration
    private FilterConfig filterConfig = null;

    // Period during which we keep a request as correctly followed the request preflight process
    private int requestPreflightCacheDelayInSeconds = 60;

    // Cache used to cache preflighted requests
    private Cache requestPreflightCache = null;

    /**
     * {@inheritDoc}
     *
     * @see Filter#init(FilterConfig)
     */
    @Override
    public void init(FilterConfig fConfig) throws ServletException {
        // Get filter configuration
        this.filterConfig = fConfig;
        // Initialize preflighted requests dedicated cache with a cache of X minutes expiration delay for each item
        PersistenceConfiguration cachePersistence = new PersistenceConfiguration();
        cachePersistence.strategy(PersistenceConfiguration.Strategy.NONE);
        CacheConfiguration cacheConfig = new CacheConfiguration().memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.FIFO).eternal(false)
                .timeToLiveSeconds(this.requestPreflightCacheDelayInSeconds)
                .statistics(false).diskExpiryThreadIntervalSeconds(this.requestPreflightCacheDelayInSeconds / 2)
                .persistence(cachePersistence).maxEntriesLocalHeap(10000).logging(false);
        cacheConfig.setName("PreflightedRequestsCacheConfig");
        this.requestPreflightCache = new Cache(cacheConfig);
        this.requestPreflightCache.setName("PreflightedRequestsCache");
        CacheManager.getInstance().addCache(this.requestPreflightCache);
    }

    /**
     * {@inheritDoc}
     *
     * @see Filter#destroy()
     */
    @Override
    public void destroy() {
        // Remove Cache
        CacheManager.getInstance().removeCache("PreflightedRequestsCache");
    }

    /**
     * {@inheritDoc}
     *
     * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest htReq = ((HttpServletRequest) request);
        HttpServletResponse htResp = ((HttpServletResponse) response);
        int accessDeniedHttpResponse = HttpServletResponse.SC_FORBIDDEN;
        String accessControlAllowMethods = "GET, POST";
        String accessControlAllowOrigin = "http://foo.example";

        /* Step 01 : Determine the type of the incoming request */
        CORSRequestPreflightType corsReqType = determineRequestType(htReq);

        /* Step 02 : Process request according to is type */
        switch (corsReqType) {
        // --HTTP request send by client to preflight a further 'Complex' request
        case REQUEST_FOR_PREFLIGHT: {
            CORSRequestPreflightData corsReq = new CORSRequestPreflightData(htReq);
            // ----Step 2a: Check that request for preflight is valid, if not, send an access denied (do not give infos about bad resquest root cause)
            if (corsReq.getOrigin().trim().isEmpty() || corsReq.getExpectedMethod().trim().isEmpty()) {
                traceInvalidRequestDetected(htReq);
                htResp.reset();
                htResp.sendError(accessDeniedHttpResponse);
                // Exit Filter : Use 'return' algorithm break in order to avoid multiple IF statement and enhance readability...
                return;

            }
            // ----Step 2b: Store preflight request data in the Cache to keep (mark) the request as correctly followed the request preflight process
            Element cachedRequest = new Element(CORSUtils.buildRequestCacheIdentifier(htReq), corsReq);
            this.requestPreflightCache.put(cachedRequest);
            // ----Step 2c: Return corresponding response - This part should be customized with application specific constraints.....
            htResp.reset();
            htResp.setStatus(HttpServletResponse.SC_OK);
            htResp.setHeader("Access-Control-Allow-Origin", accessControlAllowOrigin);
            htResp.setHeader("Access-Control-Allow-Methods", accessControlAllowMethods);
            if (!corsReq.getExpectedCustomHeaders().isEmpty()) {
                htResp.setHeader("Access-Control-Allow-Headers", corsReq.getExpectedCustomHeaders().toString().replaceFirst("\\[", "").replaceFirst("\\]", "").trim());
            }
            htResp.setIntHeader("Access-Control-Max-Age", (this.requestPreflightCacheDelayInSeconds));
            break;
        }
        // --Normal HTTP request send by client that require preflight ie 'Complex' resquest in Preflight process
        case COMPLEX_REQUEST: {
            String rid = CORSUtils.buildRequestCacheIdentifier(htReq);
            // ----Step 2a: Check if the current request has an entry into the preflighted requests Cache
            if (this.requestPreflightCache.get(rid) == null) {
                traceInvalidRequestDetected(htReq);
                htResp.reset();
                htResp.sendError(accessDeniedHttpResponse);
                // Exit Filter : Use 'return' algorithm break in order to avoid multiple IF statement and enhance readability...
                return;
            }
            // ----Step 2b: Check that preflight information declared during the preflight request match the current request on key information
            CORSRequestPreflightData corsPreflightReq = (CORSRequestPreflightData) this.requestPreflightCache.get(rid).getValue();
            String origin = CORSUtils.retrieveHeader("Origin", htReq);
            List<String> customHeaders = CORSUtils.retrieveCustomHeaders(htReq);
            boolean match = false;
            // ------Start with comparison of "Origin" HTTP header (according to utility method impl. used to retrieve header reference cannot be null)...
            if (origin.equals(corsPreflightReq.getOrigin())) {
                // ------Continue with HTTP method...
                if (accessControlAllowMethods.contains(htReq.getMethod()) && htReq.getMethod().equals(corsPreflightReq.getExpectedMethod())) {
                    // ------Finish with custom HTTP headers (use an method to avoid manual iteration on collection to increase the speed)...
                    if (customHeaders.size() == corsPreflightReq.getExpectedCustomHeaders().size()) {
                        Collections.sort(customHeaders);
                        Collections.sort(corsPreflightReq.getExpectedCustomHeaders());
                        if (customHeaders.toString().equals(corsPreflightReq.getExpectedCustomHeaders().toString())) {
                            match = true;
                        }
                    }
                }
            }
            if (match) {
                // Continue chain to next filter
                chain.doFilter(request, response);
            } else {
                traceInvalidRequestDetected(htReq);
                htResp.reset();
                htResp.sendError(accessDeniedHttpResponse);
            }
            break;
        }
        // --Normal HTTP request send by client that do not require preflight ie 'Simple' resquest in Preflight process
        case SIMPLE_REQUEST: {
            // Continue chain to next filter
            chain.doFilter(request, response);
            break;
        }
        // --Unknown HTTP request type !
        default: {
            traceInvalidRequestDetected(htReq);
            htResp.reset();
            htResp.sendError(accessDeniedHttpResponse);
            break;
        }
        }
    }

    /**
     * Internal method to determine the type of the incoming request.
     *
     * @param htReq HTTP Request
     * @return the type as enumeration item
     */
    private CORSRequestPreflightType determineRequestType(HttpServletRequest htReq) {
        CORSRequestPreflightType type = CORSRequestPreflightType.UNKNOWN;

        if ("OPTIONS".equalsIgnoreCase(htReq.getMethod())) {
            type = CORSRequestPreflightType.REQUEST_FOR_PREFLIGHT;
        } else {
            if (!CORSUtils.retrieveCustomHeaders(htReq).isEmpty()) {
                type = CORSRequestPreflightType.COMPLEX_REQUEST;
            } else if ("POST".equalsIgnoreCase(htReq.getMethod()) && !"application/x-www-form-urlencoded".equalsIgnoreCase(htReq.getContentType())
                    && !"multipart/form-data".equalsIgnoreCase(htReq.getContentType()) && !"text/plain".equalsIgnoreCase(htReq.getContentType())) {
                type = CORSRequestPreflightType.COMPLEX_REQUEST;
            } else if ("HEAD".equalsIgnoreCase(htReq.getMethod()) || "DELETE".equalsIgnoreCase(htReq.getMethod()) || "PUT".equalsIgnoreCase(htReq.getMethod())
                    || "TRACE".equalsIgnoreCase(htReq.getMethod()) || "CONNECT".equalsIgnoreCase(htReq.getMethod())) {
                type = CORSRequestPreflightType.COMPLEX_REQUEST;
            } else {
                type = CORSRequestPreflightType.SIMPLE_REQUEST;
            }

        }

        return type;
    }

    /**
     * Method to add data of invalid request detected to a trace log
     *
     * @param htReq Invalid request detected
     */
    private void traceInvalidRequestDetected(HttpServletRequest htReq) {
        // Customize trace...
        this.filterConfig.getServletContext().log("---[CORS Invalid request detected]---");
        this.filterConfig.getServletContext().log(String.format("Client Address : %s", htReq.getRemoteAddr()));
        this.filterConfig.getServletContext().log(String.format("Target URL     : %s", htReq.getRequestURL()));
        this.filterConfig.getServletContext().log(String.format("Query String   : %s", htReq.getQueryString()));
        this.filterConfig.getServletContext().log(String.format("HTTP Method    : %s", htReq.getMethod()));
        // Print more request useful data.....
        this.filterConfig.getServletContext().log("-------------------------------------");
    }
}

Sample implementation: Utility class used by Filter

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

/**
 * Utility class for CORS data retrieving & processing.
 */
public class CORSUtils {

    /**
     * Method to retrieve HTTP request custom headers list.
     *
     * @param httpRequest Source HTTP request
     * @return List of custom headers (converted to uppercase to avoid case issue)
     */
    public static List<String> retrieveCustomHeaders(HttpServletRequest httpRequest) {
        List<String> xHeaders = new ArrayList<String>();
        String name = null;

        if (httpRequest == null) {
            throw new IllegalArgumentException("HTTP Request cannot be null !");
        }

        Enumeration<String> headers = httpRequest.getHeaderNames();
        while (headers.hasMoreElements()) {
            name = headers.nextElement().toUpperCase().trim();
            if (name.startsWith("X-")) {
                xHeaders.add(name.trim());
            }
        }

        return xHeaders;

    }

    /**
     * Method to retrieve a HTTP Header value from the source HTTP request. <br/>
     * Manage Header name case issue and take only first value.
     *
     * @param headerName HTTP name
     * @param httpRequest Source HTTP request
     * @return The HTTP Header value or "" if it cannot be found
     */
    public static String retrieveHeader(String headerName, HttpServletRequest httpRequest) {
        String value = "";
        String name = null;

        if (httpRequest == null) {
            throw new IllegalArgumentException("HTTP Request cannot be null !");
        }
        if ((headerName == null) || headerName.trim().isEmpty()) {
            throw new IllegalArgumentException("HTTP header name be null !");
        }

        Enumeration<String> headers = httpRequest.getHeaderNames();
        while (headers.hasMoreElements()) {
            name = headers.nextElement();
            if (name.trim().equalsIgnoreCase(headerName)) {
                value = httpRequest.getHeader(name);
                break;
            }
        }

        return value;
    }

    /**
     * Method to build an identifier for a request into the preflighted requests cache
     *
     * @param httpRequest Source HTTP request
     * @return The ID as String
     */
    public static String buildRequestCacheIdentifier(HttpServletRequest httpRequest) {
        return (httpRequest.getRemoteAddr() + "_" + httpRequest.getRequestURI()).trim();
    }
}

Sample implementation: Pojo class used to store Preflight request key information

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

/**
 * Class to store information about a CORS preflighted request.
 */
@SuppressWarnings("serial")
public class CORSRequestPreflightData implements Serializable {

    /** Final HTTP request expected method */
    private String expectedMethod = null;
    /** Final HTTP request expected custom headers */
    private List<String> expectedCustomHeaders = null;

    /** Current HTTP request uri */
    private String uri = null;
    /** Current HTTP request origin header */
    private String origin = null;
    /** Current Sender IP address */
    private String sender = null;

    /**
     * Constructor
     *
     * @param httpRequest Source HTTP request
     */
    public CORSRequestPreflightData(HttpServletRequest httpRequest) {
        super();
        String tmp = null;
        if (httpRequest == null) {
            throw new IllegalArgumentException("HTTP request cannot be null !");
        }
        this.sender = httpRequest.getRemoteAddr();
        this.uri = httpRequest.getRequestURI();
        this.origin = CORSUtils.retrieveHeader("Origin", httpRequest);
        this.expectedMethod = CORSUtils.retrieveHeader("Access-Control-Request-Method", httpRequest);
        tmp = CORSUtils.retrieveHeader("Access-Control-Request-Headers", httpRequest);
        if (!tmp.trim().isEmpty()) {
            this.expectedCustomHeaders = new ArrayList<String>();
            String[] hs = tmp.split(",");
            for (String h : hs) {
                if ((h != null) && !h.trim().isEmpty()) {
                    this.expectedCustomHeaders.add(h.toUpperCase().trim());
                }
            }
        }
    }

    /**
     * Getter
     *
     * @return the expectedMethod
     */
    public String getExpectedMethod() {
        return this.expectedMethod;
    }

    /**
     * Getter
     *
     * @return the expectedCustomHeaders
     */
    public List<String> getExpectedCustomHeaders() {
        return this.expectedCustomHeaders;
    }

    /**
     * Getter
     *
     * @return the uri
     */
    public String getUri() {
        return this.uri;
    }

    /**
     * Getter
     *
     * @return the origin
     */
    public String getOrigin() {
        return this.origin;
    }

    /**
     * Getter
     *
     * @return the sender
     */
    public String getSender() {
        return this.sender;
    }
}

Sample implementation: Enumeration used to represents the differents CORS request type

/**
 * Enumeration of the differents CORS "request preflight" HTTP request type.
 */
public enum CORSRequestPreflightType {

    /** HTTP request send by client to preflight a further 'Complex' request */
    REQUEST_FOR_PREFLIGHT,

    /** Normal HTTP request send by client that require preflight ie 'Complex' resquest in Preflight process */
    COMPLEX_REQUEST,

    /** Normal HTTP request send by client that do not require preflight ie 'Simple' resquest in Preflight process */
    SIMPLE_REQUEST,

    /** Cannot determine request type */
    UNKNOWN;
}

Note: W3AF audit tools (http://w3af.org) contains plugins to automatically audit web application to check if they implements this type of countermeasure.

It’s very useful to include this type of tools into a web application development process in order to perform a regular automatic first level check (do not replace an manual audit and manual audit must be also conducted regularly).

Patient safety assistant

Check your symptom safely

Hi, I am RX Symptom Navigator. I can help you understand what to read next and what warning signs need care.
Warning: Do not use this in emergencies, pregnancy, severe illness, or as a substitute for a doctor. For children or teens, use with a parent/guardian and clinician.
A rural-friendly guide: warning signs, when to see a doctor, related articles, tests to discuss, and OTC safety education.
1 Symptom 2 Severity 3 Safe guidance
First safety question

Is there chest pain, breathing trouble, fainting, confusion, severe bleeding, stroke-like weakness, severe injury, or pregnancy danger sign?

Choose quickly

Browse by body area
Start here: Write or select a symptom. The guide will show warning signs, doctor guidance, diagnostic tests to discuss, OTC safety education, and related RX articles.

Important: This tool is educational only. It cannot diagnose, treat, or replace a doctor. OTC information is not a prescription. In an emergency, contact local emergency services or go to the nearest hospital.

Doctor visit helper

Prepare before seeing a doctor

A simple rural-patient checklist to help you explain symptoms clearly, ask better questions, and avoid unsafe self-treatment.

Safety note: This is not a prescription or diagnosis. For severe symptoms, pregnancy danger signs, children with serious illness, chest pain, breathing difficulty, stroke-like weakness, or major injury, seek urgent care.

Which doctor may help?

General physician, urologist, nephrologist, or gynecologist depending on symptoms.

What to tell the doctor

  • Write burning, frequency, fever, flank pain, blood in urine, pregnancy, diabetes, and previous UTI history.

Questions to ask

  • Is this UTI, stone, prostate problem, diabetes-related, or another cause?
  • Do I need urine culture before antibiotics?

Tests to discuss

  • Urine routine/microscopy
  • Urine culture for recurrent/severe infection or treatment failure
  • Blood sugar and kidney function when indicated
  • Ultrasound if stone/obstruction/recurrent symptoms

Avoid these mistakes

  • Avoid self-starting antibiotics; wrong antibiotic can cause resistance.
  • Seek urgent care for fever with flank pain, pregnancy, vomiting, confusion, or inability to pass urine.

Medicine safety and first-aid guide

This section is for patient education only. It does not replace a doctor, pharmacist, or emergency care.

Safe first steps

  • Rest, drink safe water, and observe symptoms carefully.
  • Keep a written note of symptoms, duration, temperature, medicines already taken, and allergy history.
  • Seek medical care quickly if symptoms are severe, worsening, or unusual for the patient.

OTC medicine safety

  • For mild pain or fever, ask a registered pharmacist or doctor before using common over-the-counter pain/fever medicines.
  • Do not combine multiple pain medicines without advice, especially if you have kidney disease, liver disease, stomach ulcer, asthma, pregnancy, or take blood thinners.
  • Do not give adult medicines to children unless a qualified clinician advises it.

Avoid these mistakes

  • Do not start antibiotics without a proper medical decision.
  • Do not use steroid tablets or injections casually for quick relief.
  • Do not delay emergency care because of home remedies.

Get urgent help if

  • Severe symptoms, confusion, fainting, breathing difficulty, chest pain, severe dehydration, or sudden weakness need urgent medical care.
Medicine names, dose, and timing must be decided by a qualified clinician or pharmacist after checking age, pregnancy, allergy, other diseases, and current medicines.

For rural patients and family caregivers

Patient health record and symptom diary

Write your symptoms, medicines already taken, test results, and questions before visiting a doctor. This note stays on your device unless you print or copy it.

Doctor to discuss: Doctor / qualified healthcare provider
Tests to discuss with doctor
  • Basic vital signs: temperature, pulse, blood pressure, oxygen level if needed
  • Relevant blood, urine, imaging, or specialist tests only after clinical assessment
Questions to ask
  • What is the most likely cause of my symptoms?
  • Which warning signs mean I should go to emergency care?
  • Which tests are really needed now?
  • Which medicines are safe for my age, pregnancy status, allergy, kidney/liver/stomach condition, and current medicines?

Emergency warning signs such as chest pain, severe breathing difficulty, sudden weakness, confusion, severe dehydration, major injury, or loss of bladder/bowel control need urgent medical care. Do not wait for online information.

Safe pathway to proper treatment

Patient care roadmap

Use this simple roadmap to understand the next safe steps. It is educational and does not replace examination by a doctor.

Go to emergency care if you notice:
  • Severe or rapidly worsening symptoms
  • Breathing difficulty, chest pain, fainting, confusion, severe weakness, major injury, or severe dehydration
Doctor / service to discuss: Qualified healthcare provider; specialist depends on symptoms and examination.
  1. Step 1

    Check danger signs first

    If danger signs are present, seek emergency care and do not wait for online information.

  2. Step 2

    Record the symptom story

    Write when symptoms started, severity, medicines already taken, allergies, pregnancy status, and test results.

  3. Step 3

    Visit a qualified clinician

    A doctor, nurse, or qualified healthcare provider can examine you and decide which tests or treatment are needed.

  4. Step 4

    Do only useful tests

    Do tests after clinical assessment. Avoid unnecessary tests, random antibiotics, or repeated medicines without diagnosis.

  5. Step 5

    Follow up and return early if worse

    If symptoms worsen, new warning signs appear, or treatment is not helping, return for review quickly.

Rural patient practical tips
  • Take a written symptom diary and all previous prescriptions/test reports.
  • Do not hide medicines already taken, even herbal or over-the-counter medicines.
  • Ask which warning signs mean urgent referral to hospital.

This roadmap is for education. A real diagnosis and treatment plan requires history, examination, and clinical judgment.

RX Patient Help

Ask a health question safely

Write your symptom story. A health professional or site editor can review it before any answer is prepared. This box is not for emergency care.

Emergency first: Severe chest pain, breathing trouble, unconsciousness, stroke signs, severe injury, heavy bleeding, or rapidly worsening symptoms need urgent local medical care now.

Frequently Asked Questions

Is this article a replacement for a doctor?

No. It is educational content only. Patients should consult a qualified clinician for diagnosis and treatment.

When should I seek urgent care?

Seek urgent care for severe symptoms, rapidly worsening condition, breathing difficulty, severe pain, neurological changes, or any emergency warning sign.

References

Add references, clinical guidelines, textbooks, journal articles, or trusted medical sources here. You can edit this area from the RX Article Professional Blocks panel.