Skip to main content
API Security Tutorial
CHAPTER 12 Intermediate

Understanding and Configuring CORS

Updated: May 13, 2026
20 min read

# CHAPTER 12

Understanding and Configuring CORS

1. Introduction

If you build an API on api.yoursite.com and a frontend app on app.yoursite.com, and attempt to make an AJAX request, the browser will block it and throw a massive red error in the console. Why? Because of the Same-Origin Policy. To allow modern web apps to communicate with APIs on different domains, we use CORS (Cross-Origin Resource Sharing). However, misconfiguring CORS is a massive security vulnerability. In this chapter, we will demystify CORS, understand the Preflight request, and learn how to configure safe headers in PHP.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Explain the Same-Origin Policy (SOP) and why browsers enforce it.
  • Understand the purpose and mechanics of CORS.
  • Differentiate between Simple requests and Preflight (OPTIONS) requests.
  • Identify the extreme danger of using the wildcard * in production CORS headers.
  • Write PHP code to securely configure CORS for specific allowed domains.

3. Beginner-Friendly Explanation

Imagine a strict security guard at an office building (The Web Browser). The rule is: Employees from the Marketing floor can only talk to other employees on the Marketing floor (The Same-Origin Policy).

One day, an employee from Marketing (your frontend app) wants to ask a question to the Accounting department (your API on a different domain). The security guard stops them. Before the guard lets the Marketing employee talk to Accounting, the guard calls Accounting on the phone and asks: *"Hey Accounting, do you allow people from Marketing to talk to you?"* (The Preflight Request). If Accounting says *"Yes"*, the conversation happens (CORS is configured correctly). If Accounting says *"No"*, the guard blocks the conversation.

4. Real-World Attack Scenarios

  • The Wildcard Disaster: A developer gets frustrated with CORS errors while building their app. To "fix" it, they set Access-Control-Allow-Origin: * on an API that uses cookie-based authentication. An attacker sets up an evil website freemovies.com. When a victim visits freemovies.com, the evil site makes an AJAX request in the background to the vulnerable API. Because the API allows * (everyone), the browser attaches the victim's session cookies, and the attacker steals private data!

5. The Preflight Request (OPTIONS)

When a browser makes a complex cross-origin request (like sending a JSON POST request or sending an Authorization header), it doesn't send the real request immediately.
  1. 1. The browser sends an OPTIONS request first.
  1. 2. The server responds with CORS headers detailing who is allowed to connect, and what methods are allowed.
  1. 3. If the browser is satisfied, it *then* sends the actual POST request.

6. Vulnerable vs Secure Code Examples

Vulnerable PHP (The Lazy "Fix"):

php
12345678
<?php
// DANGER: Allows ANY website on the internet to read data from this API!
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE");
header("Access-Control-Allow-Headers: Content-Type, Authorization");

// API Logic...
?>

Secure PHP (Strict Whitelisting):

php
123456789101112131415161718192021222324252627
<?php
// Define the EXACT domains allowed to access this API
$allowed_origins = [
    &#039;https://www.myfrontendapp.com',
    &#039;https://admin.myfrontendapp.com'
];

$origin = $_SERVER[&#039;HTTP_ORIGIN'] ?? '';

if (in_array($origin, $allowed_origins)) {
    // Dynamically echo back the specific approved origin
    header("Access-Control-Allow-Origin: " . $origin);
    header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
    header("Access-Control-Allow-Headers: Content-Type, Authorization");
    
    // Crucial: Allow credentials (cookies/sessions) only for whitelisted origins
    header("Access-Control-Allow-Credentials: true");
}

// Handle the Preflight Request (Exit early)
if ($_SERVER[&#039;REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit;
}

// Proceed with API Logic...
?>

7. CORS Headers Explained

  • Access-Control-Allow-Origin: Who is allowed to call the API? (e.g., https://myapp.com)
  • Access-Control-Allow-Methods: What HTTP methods can they use? (e.g., GET, POST)
  • Access-Control-Allow-Headers: What custom headers can they send? (e.g., Authorization)
  • Access-Control-Allow-Credentials: Are they allowed to send Cookies or HTTP Auth? (Must be true if your API uses sessions. Cannot be true if Origin is *).

8. Postman vs. The Browser

CRITICAL KNOWLEDGE: Postman does NOT enforce CORS! CORS is a security feature built into web browsers (Chrome, Firefox, Safari) to protect end-users. If your CORS is misconfigured, your API will work perfectly in Postman, but will completely fail when your React app tries to call it in Chrome. This confuses beginners constantly.

9. Best Practices

  • Never Use * for Authenticated APIs: Only use * for truly public APIs (like a public weather API that requires no login). If an endpoint requires user authentication, explicitly whitelist the allowed domains.
  • Whitelist, Don't Regex: Avoid using complex Regular Expressions to validate the Origin header, as they are often poorly written and can be bypassed (e.g., .*myapp\.com allows evilmyapp.com). Use a strict array of allowed strings.
  • Configure at the Server Level: While you can do this in PHP, it is often more efficient to configure CORS headers in your Nginx or Apache server configuration blocks.

10. Common Mistakes

  • Reflecting the Origin Blindly:
php
12
// DANGEROUS: Echoes whatever the attacker sends!
header("Access-Control-Allow-Origin: " . $_SERVER[&#039;HTTP_ORIGIN']); 

This entirely defeats the purpose of CORS, as it basically creates a dynamic wildcard.

  • Forgetting the OPTIONS request: If your PHP script requires a JWT token, but you don't return 200 OK early for the OPTIONS request, the browser's preflight check will fail (because preflight requests don't send the JWT).

11. Mini Exercises

  1. 1. Does Postman enforce CORS policies?
  1. 2. What HTTP method does a browser use to perform a "Preflight" check?

12. Practice Challenges

Challenge: Set up a simple PHP file that returns {"status": "ok"}. Open a totally different website in your browser (like google.com), open the developer console (F12), and use fetch('http://localhost/your_api.php'). Observe the CORS error. Now, add the correct headers to your PHP file to allow google.com specifically, and try the fetch command again.

13. MCQs with Answers

Question 1

What is the fundamental browser security mechanism that CORS is designed to selectively bypass?

Question 2

Why is setting Access-Control-Allow-Origin: * considered a severe security risk on APIs that handle sensitive user data?

Question 3

If a web application makes a cross-origin POST request with a custom Authorization header, what must the API server do first?

14. Interview Questions

  • Q: Explain the concept of CORS. Why do browsers enforce it, and why doesn't Postman enforce it?
  • Q: Walk me through the sequence of HTTP requests when a browser initiates a Preflight CORS check.
  • Q: A developer is receiving a CORS error and suggests setting Access-Control-Allow-Origin: * to fix it quickly. Explain why this is dangerous and how to fix it securely.

15. FAQs

Q: My mobile app (iOS/Android) is getting CORS errors. How do I fix this? A: Native mobile apps do not run in web browsers, so they technically do not enforce CORS. However, if you are building a mobile app using a web-view framework like Ionic, React Native, or Cordova, the underlying web-view *does* enforce CORS. You must whitelist localhost or the specific scheme the app uses (e.g., ionic://localhost).

16. Summary

In this chapter, we solved the mystery of the dreaded CORS error. We learned that the Same-Origin Policy is a crucial browser protection mechanism, and CORS is the negotiated handshake (via OPTIONS preflight requests) to safely bypass it. We emphasized the catastrophic danger of using the wildcard * on authenticated APIs, and demonstrated how to write strict PHP whitelists to ensure only trusted frontend applications can communicate with our backend.

17. Next Chapter Recommendation

Even if the traffic is coming from a trusted origin, what happens if that origin sends 10,000 requests in a single second? Proceed to Chapter 13: API Rate Limiting and Throttling to learn how to protect your server from exhaustion.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·