REST API: Stop rest_send_cors_headers() clobbering Access-Control-Allow-Methods, and make the list filterable. - #13151
Conversation
`Access-Control-Allow-Methods` is the only CORS list header REST API responses send that has no extension point. Its two siblings gained one in 5.5.0 -- `rest_exposed_cors_headers` and `rest_allowed_cors_headers`, both array-filtered then imploded in `WP_REST_Server::send_headers()`, and both given the request in context in 6.3.0. This one was missed each time, because it is sent from `rest_send_cors_headers()` rather than from the server. Adds a filter with the same shape, so a site can compose the list rather than unhook `rest_send_cors_headers` and reimplement it. `$request` is available because `rest_pre_serve_request` already passes it; the filter accepts three arguments to reach it. The filter runs only when the response has not set the header itself, which is the preceding change on this branch. A value on the response is the more specific instruction, and there is no default left to shape once it is present. Adds three tests to the existing CORS header coverage. See #46992.
Keeps the two facts the code does not show -- that this runs after the response's own headers have been sent, and that `header()` replaces by default -- and drops the sentence restating the conditional below it. See #46992. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
Trac ticket: https://core.trac.wordpress.org/ticket/46992 — background, prior art and the caveat are there.
Approach discussed in person with @adamsilverstein, who closed the ticket in 2019 and has since agreed on option C.
Changes
1. Don't clobber a value set on the response.
rest_send_cors_headers()runs onrest_pre_serve_request, after the response's own headers have been sent, andheader()replaces by default — so the hardcoded list discarded anything set viarest_post_dispatch.get_headers()rather thanheaders_list(): it reads what the response intends rather than what PHP already emitted, and needs no Xdebug under CLI.2. Add
rest_allowed_cors_methods. Same shape as the sibling CORS list filtersrest_exposed_cors_headersandrest_allowed_cors_headers— array in, imploded out,$requestin context.rest_pre_serve_requestalready passes$request, henceadd_filter( ..., 10, 3 ).The filter runs only when the response has not set the header — a value on the response is the more specific instruction, and there is no default left to shape.
Both are needed: (1) alone forces a plugin to restate the whole list, so two plugins conflict instead of composing; (2) alone still gets discarded when a response sets its own value.
The default output is unchanged for every site.
Tests
rest_send_cors_headers()had no coverage at all. New file, 10 tests / 46 assertions — both changes plus baseline behavior (Origin present and absent, default output,Varystill appending,Access-Control-Allow-Originand-Credentialsnot response-settable).rest-api.php.--group restapi: 3560 / 16285, no new failures against a 3550 / 16239 baseline. The one error in both runs (Test_oEmbed_Controller::test_proxy_with_classic_embed_provider) is pre-existing on trunk.