@@ -1076,9 +1076,10 @@ will make the browser remove its local cookie::
1076
1076
Setting Cross Origin Request Headers (CORS)
1077
1077
-------------------------------------------
1078
1078
1079
- The ``cors() `` method is used to define `HTTP Access Control
1079
+ The ``cors() `` method returns a ``CorsBuilder `` instance which provides a fluent
1080
+ interface for defining `HTTP Access Control
1080
1081
<https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS> `__
1081
- related headers with a fluent interface ::
1082
+ related headers::
1082
1083
1083
1084
$this->response = $this->response->cors($this->request)
1084
1085
->allowOrigin(['*.cakephp.org'])
@@ -1095,11 +1096,173 @@ criteria are met:
1095
1096
#. The request has an ``Origin `` header.
1096
1097
#. The request's ``Origin `` value matches one of the allowed Origin values.
1097
1098
1098
- .. tip ::
1099
+ CorsBuilder Methods
1100
+ ~~~~~~~~~~~~~~~~~~~
1099
1101
1100
- CakePHP has no built-in CORS middleware because dealing with CORS requests
1101
- is very application specific. We recommend you build your own ``CORSMiddleware ``
1102
- if you need one and adjust the response object as desired.
1102
+ .. php :class :: CorsBuilder
1103
+
1104
+ The ``CorsBuilder `` provides the following methods for configuring CORS:
1105
+
1106
+ .. php :method :: allowOrigin(array|string $domains)
1107
+
1108
+ Set the list of allowed domains. You can use wildcards ``*.example.com `` to
1109
+ accept subdomains, or ``* `` to allow all domains::
1110
+
1111
+ // Allow a specific domain
1112
+ ->allowOrigin('https://example.com')
1113
+
1114
+ // Allow multiple domains
1115
+ ->allowOrigin(['https://example.com', 'https://app.example.com'])
1116
+
1117
+ // Allow all subdomains
1118
+ ->allowOrigin(['*.example.com'])
1119
+
1120
+ // Allow all origins (use with caution!)
1121
+ ->allowOrigin('*')
1122
+
1123
+ .. php :method :: allowMethods(array $methods)
1124
+
1125
+ Set the list of allowed HTTP methods::
1126
+
1127
+ ->allowMethods(['GET', 'POST', 'PUT', 'DELETE'])
1128
+
1129
+ .. php :method :: allowHeaders(array $headers)
1130
+
1131
+ Define which headers can be sent in CORS requests::
1132
+
1133
+ ->allowHeaders(['X-CSRF-Token', 'Content-Type', 'Authorization'])
1134
+
1135
+ .. php :method :: allowCredentials()
1136
+
1137
+ Enable cookies to be sent in CORS requests. This sets the
1138
+ ``Access-Control-Allow-Credentials `` header to ``true ``::
1139
+
1140
+ ->allowCredentials()
1141
+
1142
+ .. php :method :: exposeHeaders(array $headers)
1143
+
1144
+ Define which headers the client library/browser can expose to scripting::
1145
+
1146
+ ->exposeHeaders(['X-Total-Count', 'Link'])
1147
+
1148
+ .. php :method :: maxAge(string|int $age)
1149
+
1150
+ Define how long preflight OPTIONS requests are valid for (in seconds)::
1151
+
1152
+ ->maxAge(3600) // Cache preflight for 1 hour
1153
+
1154
+ .. php :method :: build()
1155
+
1156
+ Apply the configured headers to the response and return it. This must be
1157
+ called to actually apply the CORS headers::
1158
+
1159
+ $response = $corsBuilder->build();
1160
+
1161
+ Practical CORS Examples
1162
+ ~~~~~~~~~~~~~~~~~~~~~~~
1163
+
1164
+ Here are some common CORS configurations:
1165
+
1166
+ **API accepting requests from a SPA frontend **::
1167
+
1168
+ // In your controller
1169
+ public function beforeFilter(EventInterface $event)
1170
+ {
1171
+ parent::beforeFilter($event);
1172
+
1173
+ if ($this->request->is('options')) {
1174
+ // Handle preflight requests
1175
+ $this->response = $this->response->cors($this->request)
1176
+ ->allowOrigin(['https://app.example.com'])
1177
+ ->allowMethods(['GET', 'POST', 'PUT', 'DELETE'])
1178
+ ->allowHeaders(['Content-Type', 'Authorization'])
1179
+ ->allowCredentials()
1180
+ ->maxAge(86400)
1181
+ ->build();
1182
+
1183
+ return $this->response;
1184
+ }
1185
+ }
1186
+
1187
+ public function index()
1188
+ {
1189
+ // Apply CORS to regular requests
1190
+ $this->response = $this->response->cors($this->request)
1191
+ ->allowOrigin(['https://app.example.com'])
1192
+ ->allowCredentials()
1193
+ ->build();
1194
+
1195
+ // Your regular controller logic...
1196
+ }
1197
+
1198
+ **Public API with relaxed CORS **::
1199
+
1200
+ $this->response = $this->response->cors($this->request)
1201
+ ->allowOrigin('*')
1202
+ ->allowMethods(['GET'])
1203
+ ->exposeHeaders(['X-Total-Count', 'X-Page'])
1204
+ ->maxAge(3600)
1205
+ ->build();
1206
+
1207
+ Creating CORS Middleware
1208
+ ~~~~~~~~~~~~~~~~~~~~~~~~
1209
+
1210
+ For consistent CORS handling across your application, create a middleware::
1211
+
1212
+ // src/Middleware/CorsMiddleware.php
1213
+ namespace App\Middleware;
1214
+
1215
+ use Psr\Http\Message\ResponseInterface;
1216
+ use Psr\Http\Message\ServerRequestInterface;
1217
+ use Psr\Http\Server\MiddlewareInterface;
1218
+ use Psr\Http\Server\RequestHandlerInterface;
1219
+
1220
+ class CorsMiddleware implements MiddlewareInterface
1221
+ {
1222
+ public function process(
1223
+ ServerRequestInterface $request,
1224
+ RequestHandlerInterface $handler
1225
+ ): ResponseInterface {
1226
+ // Handle preflight requests
1227
+ if ($request->getMethod() === 'OPTIONS') {
1228
+ $response = new \Cake\Http\Response();
1229
+ $response = $response->cors($request)
1230
+ ->allowOrigin(['*.myapp.com'])
1231
+ ->allowMethods(['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'])
1232
+ ->allowHeaders(['Content-Type', 'Authorization'])
1233
+ ->allowCredentials()
1234
+ ->maxAge(3600)
1235
+ ->build();
1236
+
1237
+ return $response;
1238
+ }
1239
+
1240
+ $response = $handler->handle($request);
1241
+
1242
+ // Add CORS headers to regular requests
1243
+ return $response->cors($request)
1244
+ ->allowOrigin(['*.myapp.com'])
1245
+ ->allowCredentials()
1246
+ ->build();
1247
+ }
1248
+ }
1249
+
1250
+ Then add it to your application middleware stack in ``src/Application.php ``::
1251
+
1252
+ public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
1253
+ {
1254
+ $middlewareQueue
1255
+ // Add CORS middleware early in the stack
1256
+ ->add(new \App\Middleware\CorsMiddleware())
1257
+ // ... other middleware
1258
+ ->add(new ErrorHandlerMiddleware(Configure::read('Error')))
1259
+ ->add(new AssetMiddleware([
1260
+ 'cacheTime' => Configure::read('Asset.cacheTime'),
1261
+ ]))
1262
+ ->add(new RoutingMiddleware($this));
1263
+
1264
+ return $middlewareQueue;
1265
+ }
1103
1266
1104
1267
Running logic after the Response has been sent
1105
1268
----------------------------------------------
0 commit comments