Google App Engine supports multiple runtime environments. Below are modern, secure CORS implementations for Python, Java, and Go.
Access-Control-Allow-Origin: * allows any website to access your resources. Always specify exact origins in production.
Install the Flask-CORS library:
pip install flask flask-cors
Configure CORS in your Flask application:
# main.py
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
# Secure CORS configuration
CORS(app, origins=[
'https://example.com',
'https://app.example.com'
], supports_credentials=True)
@app.route('/api/data')
def get_data():
return {'data': 'value'}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
For more control, implement CORS manually:
# main.py
from flask import Flask, request
app = Flask(__name__)
ALLOWED_ORIGINS = [
'https://example.com',
'https://app.example.com'
]
@app.before_request
def handle_cors():
origin = request.headers.get('Origin', '')
if origin in ALLOWED_ORIGINS:
@app.after_request
def add_cors_headers(response):
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Credentials'] = 'true'
response.headers['Vary'] = 'Origin'
return response
if request.method == 'OPTIONS':
response = app.make_response('')
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
response.headers['Access-Control-Max-Age'] = '86400'
return response, 204
@app.route('/api/data')
def get_data():
return {'data': 'value'}
app.yaml:
runtime: python39 handlers: - url: /.* script: auto
Implement CORS using middleware:
// main.go
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
var allowedOrigins = []string{
"https://example.com",
"https://app.example.com",
}
func corsMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
// Validate origin
for _, allowed := range allowedOrigins {
if origin == allowed {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Vary", "Origin")
break
}
}
// Handle preflight
if r.Method == "OPTIONS" {
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
w.Header().Set("Access-Control-Max-Age", "86400")
w.WriteHeader(http.StatusNoContent)
return
}
next(w, r)
}
}
func dataHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"message": "CORS enabled",
})
}
func main() {
http.HandleFunc("/api/data", corsMiddleware(dataHandler))
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
fmt.Printf("Server listening on port %s\n", port)
http.ListenAndServe(":"+port, nil)
}
app.yaml:
runtime: go118
// Application.java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://example.com", "https://app.example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("Content-Type", "Authorization")
.allowCredentials(true)
.maxAge(86400);
}
};
}
}
@WebFilter(urlPatterns = {"/api/*"})
public class CorsFilter implements Filter {
private static final String[] ALLOWED_ORIGINS = {
"https://example.com",
"https://app.example.com"
};
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String origin = request.getHeader("Origin");
// Validate origin
if (origin != null && Arrays.asList(ALLOWED_ORIGINS).contains(origin)) {
response.addHeader("Access-Control-Allow-Origin", origin);
response.addHeader("Access-Control-Allow-Credentials", "true");
response.addHeader("Vary", "Origin");
}
// Handle preflight
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.addHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
response.addHeader("Access-Control-Max-Age", "86400");
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
return;
}
chain.doFilter(req, res);
}
}
For production deployments, use environment variables to manage allowed origins:
# app.yaml env_variables: CORS_ALLOWED_ORIGINS: "https://example.com,https://app.example.com"
Access in Python:
import os
origins = os.environ.get('CORS_ALLOWED_ORIGINS', '').split(',')
Access in Go:
import (
"os"
"strings"
)
origins := strings.Split(os.Getenv("CORS_ALLOWED_ORIGINS"), ",")
For comprehensive testing instructions including curl commands, browser DevTools usage, and troubleshooting common CORS errors, see the CORS Testing Guide.
The content on this site stays fresh thanks to help from users like you! If you have suggestions or would like to contribute, fork us on GitHub.
Save 39% on CORS in Action with promotional code hossainco at manning.com/hossain