Skip to main content
Version: 0.4.0

CORS Setup and Configuration

This tutorial will guide you through setting up Cross-Origin Resource Sharing (CORS) in your Gonyx application. You'll learn how to configure CORS middleware to control which domains can access your API, what HTTP methods are allowed, and how to handle preflight requests.

Prerequisites

Before starting this tutorial, make sure you have:

  • Completed the Quick Start tutorial
  • Basic understanding of HTTP and REST APIs
  • Gonyx Framework v0.4.0 or later installed
  • Familiarity with web security concepts

What is CORS?

Cross-Origin Resource Sharing (CORS) is a security mechanism that allows web browsers to make requests to servers on different domains than the one serving the web page. By default, browsers restrict cross-origin HTTP requests for security reasons.

Why You Need CORS

When building APIs, you typically need to enable CORS when:

  • Your frontend application is hosted on a different domain than your API
  • You want to allow specific external services to access your API
  • You're developing locally with different ports for frontend and backend
  • You need to control which HTTP methods and headers are allowed

Common Use Cases

  • Frontend-Backend Separation: Your React/Vue/Angular app runs on https://app.example.com while your API runs on https://api.example.com
  • Development Environment: Frontend on localhost:3000 and API on localhost:8000
  • Third-Party Integration: Allowing specific partner domains to access your API

Understanding CORS in Gonyx

Gonyx provides a built-in CORS middleware that can be easily configured through your HTTP server configuration. The middleware is built on top of the official gin-contrib/cors package, which is the standard CORS middleware for Gin framework.

How Gonyx Implements CORS

The Gonyx framework integrates the gin-contrib/cors middleware seamlessly into its configuration system, allowing you to configure CORS through JSON configuration files instead of writing Go code. This approach provides:

  • Configuration-driven setup: Define CORS rules in your config files
  • Environment-specific rules: Different CORS settings for dev/staging/prod
  • Zero code changes: Update CORS policies without modifying your application code
  • Built on industry standards: Uses the official Gin CORS middleware under the hood

The middleware handles:

  • Preflight Requests: Automatic handling of OPTIONS requests
  • Origin Validation: Checking if the requesting origin is allowed
  • Header Management: Setting appropriate CORS headers in responses
  • Credentials Support: Configuring cookie and authorization support

Step 1: Basic CORS Configuration

The CORS configuration is defined in your HTTP server configuration file. In a typical Gonyx project, this is located at configs/dev/http.json.

Minimal Configuration

Here's a minimal CORS configuration that allows all origins (useful for development):

configs/dev/http.json
{
"default": "s1",
"servers": [
{
"name": "s1",
"addr": ":3000",
"versions": ["v1"],
"support_static": false,
"conf": {
"read_timeout": -1,
"write_timeout": -1,
"request_methods": ["ALL"]
},
"middlewares": {
"order": ["logger", "cors"],
"cors": {
"allow_all_origins": true,
"allow_origins": ["*"],
"allow_methods": ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"],
"allow_private_network": false,
"allow_headers": [],
"allow_credentials": false,
"expose_headers": [],
"max_age": 0,
"allow_wildcard": true,
"allow_browser_extension": false,
"custom_schemes": [],
"allow_websockets": false,
"allow_files": false,
"option_response_status_code": 204
}
}
}
]
}
Development Only

The configuration above with allow_all_origins: true is convenient for development but should not be used in production. Always specify explicit allowed origins for production environments.

Step 2: Production-Ready CORS Configuration

For production environments, you should explicitly specify which origins are allowed to access your API:

configs/prod/http.json
{
"default": "s1",
"servers": [
{
"name": "s1",
"addr": ":3000",
"versions": ["v1"],
"support_static": false,
"conf": {
"read_timeout": -1,
"write_timeout": -1,
"request_methods": ["ALL"]
},
"middlewares": {
"order": ["logger", "cors"],
"cors": {
"allow_all_origins": false,
"allow_origins": [
"https://app.example.com",
"https://www.example.com",
"https://admin.example.com"
],
"allow_methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"allow_private_network": false,
"allow_headers": [
"Authorization",
"Content-Type",
"Accept",
"Origin",
"X-Requested-With"
],
"allow_credentials": true,
"expose_headers": ["X-Total-Count"],
"max_age": 86400,
"allow_wildcard": false,
"allow_browser_extension": false,
"custom_schemes": [],
"allow_websockets": false,
"allow_files": false,
"option_response_status_code": 204
}
}
}
]
}

This configuration:

  • Explicitly lists allowed origins
  • Restricts HTTP methods to only those needed
  • Specifies which headers clients can send
  • Enables credentials (cookies/authorization)
  • Caches preflight responses for 24 hours (86400 seconds)

Step 3: Understanding CORS Configuration Options

Let's explore each CORS configuration option in detail:

Core Settings

allow_all_origins (boolean)

When set to true, allows requests from any origin. Use with caution!

"allow_all_origins": false  // Recommended for production

allow_origins (array of strings)

List of specific origins allowed to access your API. Each origin must include the protocol.

"allow_origins": [
"https://app.example.com",
"https://localhost:3000",
"http://192.168.1.100:8080"
]
Wildcard Domains

You can use wildcards in domain names when allow_wildcard is enabled:

"allow_origins": ["https://*.example.com"]

This allows any subdomain of example.com.

allow_methods (array of strings)

HTTP methods that are allowed for cross-origin requests.

"allow_methods": ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]

Common configurations:

  • Read-only API: ["GET", "HEAD", "OPTIONS"]
  • Full CRUD API: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]
  • Custom methods: Include any custom HTTP methods your API uses

Header Configuration

allow_headers (array of strings)

Headers that the browser is allowed to send in the actual request.

"allow_headers": [
"Authorization",
"Content-Type",
"Accept",
"Origin",
"X-Requested-With",
"X-API-Key"
]

Common headers to allow:

  • Authorization - For JWT or other auth tokens
  • Content-Type - Required for POST/PUT requests with JSON
  • Accept - Content negotiation
  • Custom headers like X-API-Key, X-Tenant-ID, etc.
note

If allow_headers is empty, the middleware will allow all headers from the request.

expose_headers (array of strings)

Headers that the browser is allowed to access in the response.

"expose_headers": [
"X-Total-Count",
"X-Page-Number",
"X-Rate-Limit-Remaining"
]

By default, browsers only expose simple response headers. Use this to expose custom headers like pagination info or rate limits.

Credentials and Security

allow_credentials (boolean)

Whether to allow requests with credentials (cookies, HTTP authentication, client-side SSL certificates).

"allow_credentials": true
Important

When allow_credentials is true, you cannot use allow_all_origins: true or wildcard origins. You must specify explicit origins.

allow_private_network (boolean)

Whether to allow requests from private networks (as defined by the Private Network Access specification).

"allow_private_network": false

Enable this only if you need to allow requests from private IP addresses or localhost in production.

Advanced Options

max_age (integer)

How long (in seconds) the browser should cache the preflight response.

"max_age": 86400  // 24 hours
  • 0 - Browser makes a preflight request for every actual request
  • 86400 - Cache for 24 hours (recommended for production)
  • -1 - Disable preflight caching
Performance

Setting a reasonable max_age (e.g., 1-24 hours) reduces the number of preflight requests, improving performance.

allow_wildcard (boolean)

Enable wildcard domain matching in allow_origins.

"allow_wildcard": true

When enabled, you can use patterns like:

  • https://*.example.com - Any subdomain
  • https://app-*.example.com - Specific pattern

option_response_status_code (integer)

Status code to return for successful OPTIONS (preflight) requests.

"option_response_status_code": 204

Standard values:

  • 204 - No Content (recommended)
  • 200 - OK (also acceptable)

Special Use Cases

allow_browser_extension (boolean)

Allow requests from browser extensions.

"allow_browser_extension": false

Set to true if you're building a browser extension that needs to access your API.

allow_websockets (boolean)

Enable CORS for WebSocket connections.

"allow_websockets": false

Required if your API uses WebSocket connections across domains.

allow_files (boolean)

Allow file:// protocol origins (local files).

"allow_files": false
Security Risk

Enabling this can be a security risk. Only enable for specific development or testing scenarios.

custom_schemes (array of strings)

Allow custom URL schemes beyond http/https.

"custom_schemes": ["app://", "mycustomapp://"]

Useful for native mobile apps or desktop applications with custom URL schemes.

Step 4: Middleware Ordering

The order of middlewares matters! CORS should typically come early in the middleware chain but after the logger:

"middlewares": {
"order": ["logger", "cors", "auth", "rate-limit"],
"logger": { ... },
"cors": { ... },
"auth": { ... },
"rate-limit": { ... }
}

Why Order Matters

  1. Logger first: Logs all requests including CORS preflight requests
  2. CORS early: Handles preflight requests before they reach authentication
  3. Auth after CORS: OPTIONS requests don't need authentication
  4. Rate limiting last: Applied after CORS validation

Step 5: Testing Your CORS Configuration

After configuring CORS, you should test it to ensure it works correctly.

Testing with cURL

Test a simple GET request:

curl -H "Origin: https://app.example.com" \
-H "Access-Control-Request-Method: GET" \
-H "Access-Control-Request-Headers: Content-Type" \
-X OPTIONS \
http://localhost:3000/api/v1/Sample/hello \
-v

Look for these response headers:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 86400

Testing with a Frontend Application

Create a simple HTML file to test CORS:

test-cors.html
<!DOCTYPE html>
<html>
<head>
<title>CORS Test</title>
</head>
<body>
<h1>CORS Test</h1>
<button id="testGet">Test GET Request</button>
<button id="testPost">Test POST Request</button>
<div id="result"></div>

<script>
const apiUrl = 'http://localhost:3000/api/v1/Sample';

document.getElementById('testGet').addEventListener('click', async () => {
try {
const response = await fetch(`${apiUrl}/hello`);
const data = await response.text();
document.getElementById('result').innerHTML =
`<p style="color: green;">GET Success: ${data}</p>`;
} catch (error) {
document.getElementById('result').innerHTML =
`<p style="color: red;">GET Error: ${error.message}</p>`;
}
});

document.getElementById('testPost').addEventListener('click', async () => {
try {
const response = await fetch(`${apiUrl}/test-cors`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ test: 'data', timestamp: Date.now() })
});
const data = await response.json();
document.getElementById('result').innerHTML =
`<p style="color: green;">POST Success: ${JSON.stringify(data)}</p>`;
} catch (error) {
document.getElementById('result').innerHTML =
`<p style="color: red;">POST Error: ${error.message}</p>`;
}
});
</script>
</body>
</html>

Open this file in your browser and click the buttons to test CORS.

Common CORS Errors

Error: "No 'Access-Control-Allow-Origin' header is present"

Solution: Ensure the requesting origin is in your allow_origins list.

Error: "Credential is not supported if the CORS header 'Access-Control-Allow-Origin' is '*'"

Solution: Set allow_all_origins: false and specify explicit origins when using allow_credentials: true.

Error: "Method [METHOD] is not allowed by Access-Control-Allow-Methods"

Solution: Add the required HTTP method to your allow_methods array.

Error: "Request header field [HEADER] is not allowed by Access-Control-Allow-Headers"

Solution: Add the required header to your allow_headers array.

Step 6: Environment-Specific Configuration

You should maintain different CORS configurations for different environments:

Development Configuration

configs/dev/http.json
{
"middlewares": {
"cors": {
"allow_all_origins": true,
"allow_origins": ["*"],
"allow_methods": ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"],
"allow_credentials": false,
"max_age": 0
}
}
}

Staging Configuration

configs/staging/http.json
{
"middlewares": {
"cors": {
"allow_all_origins": false,
"allow_origins": [
"https://staging-app.example.com",
"https://staging-admin.example.com"
],
"allow_methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"allow_credentials": true,
"max_age": 3600
}
}
}

Production Configuration

configs/prod/http.json
{
"middlewares": {
"cors": {
"allow_all_origins": false,
"allow_origins": [
"https://app.example.com",
"https://www.example.com",
"https://admin.example.com"
],
"allow_methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"allow_credentials": true,
"allow_headers": [
"Authorization",
"Content-Type",
"Accept"
],
"expose_headers": ["X-Total-Count"],
"max_age": 86400,
"allow_wildcard": false
}
}
}

Best Practices

Security Best Practices

  1. Never use allow_all_origins: true in production

    • Always specify explicit origins
    • Use environment-specific configurations
  2. Be restrictive with allowed methods

    • Only allow methods your API actually uses
    • Remove OPTIONS from the list if you're handling it automatically
  3. Carefully consider credentials

    • Only enable allow_credentials if you need cookies or auth headers
    • Never combine credentials with wildcard origins
  4. Validate origins strictly

    • Don't use overly broad wildcard patterns
    • Regularly review and update allowed origins
  5. Use HTTPS in production

    • Always use https:// origins in production
    • HTTP origins can be security vulnerabilities

Performance Best Practices

  1. Set appropriate max_age

    • Use 1-24 hours in production to reduce preflight requests
    • Consider user experience vs. security tradeoffs
  2. Minimize allowed headers

    • Only allow headers your API actually needs
    • Reduces preflight complexity
  3. Enable wildcard matching carefully

    • Use for legitimate subdomain patterns only
    • Don't over-rely on wildcards

Development Best Practices

  1. Use different configs per environment

    • Maintain separate CORS configs for dev/staging/prod
    • Use environment variables when needed
  2. Document your CORS requirements

    • Keep a list of all domains that need access
    • Document why each origin is allowed
  3. Test thoroughly

    • Test preflight requests
    • Test actual requests with various methods
    • Test with and without credentials
  4. Monitor CORS errors

    • Log CORS-related errors
    • Set up alerts for unusual CORS failures

Common Scenarios

Scenario 1: Single-Page Application (SPA)

Frontend on https://app.example.com, API on https://api.example.com:

"cors": {
"allow_all_origins": false,
"allow_origins": ["https://app.example.com"],
"allow_methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"allow_credentials": true,
"allow_headers": ["Authorization", "Content-Type"],
"max_age": 86400
}

Scenario 2: Mobile App with Custom Scheme

Mobile app using myapp:// scheme:

"cors": {
"allow_all_origins": false,
"allow_origins": ["myapp://"],
"custom_schemes": ["myapp://"],
"allow_methods": ["GET", "POST", "PUT", "DELETE"],
"allow_credentials": false
}

Scenario 3: Multiple Subdomains

All subdomains of example.com need access:

"cors": {
"allow_all_origins": false,
"allow_origins": ["https://*.example.com"],
"allow_wildcard": true,
"allow_methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"allow_credentials": true,
"max_age": 86400
}

Scenario 4: Public API

Public API with no authentication:

"cors": {
"allow_all_origins": true,
"allow_origins": ["*"],
"allow_methods": ["GET", "OPTIONS"],
"allow_credentials": false,
"max_age": 86400
}

Scenario 5: API with Rate Limiting Headers

API that exposes rate limit information:

"cors": {
"allow_all_origins": false,
"allow_origins": ["https://app.example.com"],
"allow_methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"allow_credentials": true,
"expose_headers": [
"X-RateLimit-Limit",
"X-RateLimit-Remaining",
"X-RateLimit-Reset"
],
"max_age": 3600
}

Troubleshooting

CORS Still Not Working?

  1. Check middleware order

    • Ensure CORS middleware is registered
    • Verify it's in the correct position
  2. Verify configuration syntax

    • Check for JSON syntax errors
    • Ensure all required fields are present
  3. Test preflight separately

    • Use browser dev tools to inspect OPTIONS requests
    • Check response headers
  4. Review server logs

    • Look for CORS-related errors
    • Check if requests are reaching your handlers
  5. Clear browser cache

    • Browsers cache preflight responses
    • Clear cache or use incognito mode for testing

Debugging Tips

Enable verbose logging to see CORS decisions:

"middlewares": {
"order": ["logger", "cors"],
"logger": {
"format": "> [${time}] ${status} - ${latency} ${method} ${path} ${queryParams}\n",
"output": "stdout"
},
"cors": { ... }
}

Use browser developer tools:

  1. Open DevTools (F12)
  2. Go to Network tab
  3. Look for OPTIONS requests
  4. Check request and response headers

Conclusion

In this tutorial, you learned how to:

  • Understand what CORS is and why it's important
  • Configure CORS middleware in Gonyx Framework
  • Use different CORS configurations for development and production
  • Test CORS functionality
  • Troubleshoot common CORS issues
  • Apply best practices for security and performance

CORS is a critical security feature for modern web APIs. By properly configuring CORS in your Gonyx application, you ensure that your API is both secure and accessible to legitimate clients.

Next Steps

  • Add authentication middleware after CORS
  • Implement rate limiting to protect your API
  • Set up API monitoring to track CORS failures
  • Configure additional security headers
  • Explore API versioning strategies

Additional Resources

Official Documentation

CORS Standards & Specifications

Security Resources