Google Cloud Platform: Hooking Up WordPress Stateless CDN REST API

WordPress can publish content through an API and deliver images from Google’s infrastructure without making every request pass through your web server. That separation is useful whether you run a conventional WordPress site, build a JavaScript front end, or prepare to run several WordPress instances behind a load balancer.
The trick is understanding what each component actually does. WP-Stateless moves media into Google Cloud Storage. Cloud CDN caches that media closer to visitors. The WordPress REST API exposes posts and attachments as JSON. Together, they reduce your application server’s responsibility for storing and delivering uploads, but they do not automatically turn WordPress into a serverless application.
This guide reflects a 2019 setup; plugin options, Google Cloud interfaces, and authentication tools may have changed since then.
What We Are Building
The target architecture has two separate request paths:
Content:
Browser or application → WordPress REST API → WordPress database
Media:
Browser → media.example.com → Google Cloud CDN → Cloud Storage bucket
Uploads:
Authorized editor → WordPress → WP-Stateless → Cloud Storage bucket
WordPress remains responsible for users, permissions, attachment records, and content. Cloud Storage becomes the durable home for uploaded files. A Google HTTP(S) load balancer, configured with a backend bucket and Cloud CDN, supplies the CDN delivery path.
This distinction matters: a Cloud Storage URL is not, by itself, a configured Cloud CDN distribution. Offloading media and enabling CDN caching are separate tasks.
Likewise, “stateless” here describes media storage. Your database, plugin configuration, sessions, caches, and any other files written locally still need attention before you can safely scale WordPress across interchangeable servers.
Before You Start
Use a staging site first, and have these prerequisites ready:
- A working WordPress installation with HTTPS and administrator access.
- A Google Cloud project with billing enabled.
- Permission to create a storage bucket, service account, and load-balancing resources.
- Control of a domain such as
media.example.com. - Backups of both the WordPress database and
wp-content/uploads. - A supported WP-Stateless release, checked against your WordPress and PHP versions.
This walkthrough assumes your media is public. Do not apply its public-read configuration to private documents, paid downloads, or files containing personal information. Those require an authenticated delivery design, not an ordinary public media bucket.
1. Create a Cloud Storage Bucket
In the Google Cloud Console, open Cloud Storage and create a bucket with a globally unique name, such as example-wp-media-2019.
Choose its location based on your application, audience, availability needs, and budget. A CDN reduces repeated origin fetches, but the bucket’s location still affects uploads and cache misses. Standard storage is a sensible starting point for frequently accessed website media.
Keep unrelated application data in a different bucket. That makes access controls, cleanup, and cost attribution easier to reason about.
Decide How Public Reads Will Work
For this public-media architecture, visitors and the CDN must be able to read uploaded objects. Depending on the bucket access-control model, public reads can be granted through bucket IAM or object ACLs.
One bucket-level approach is granting allUsers the Storage Object Viewer role. Be aware that this role also permits object listing; do not assume an unlinked filename makes a file private.
Never grant public write access. Also check your WP-Stateless release’s expectations: a plugin that tries to set per-object ACLs can fail when the bucket’s access-control configuration disallows those operations.
Test with a harmless file before uploading the entire library.
2. Create Credentials for WP-Stateless
Create a dedicated service account for the WordPress media integration. Give it permissions on this bucket rather than broad project-wide ownership.
The integration must be able to upload objects and usually read, replace, and delete them. Storage Object Admin at the bucket level is a practical starting point for those object operations. Some plugin releases or setup workflows may also need bucket metadata permissions; verify the documented requirements and add only what is missing.
For a setup using a JSON service-account key:
- Generate a key for the dedicated account.
- Transfer it securely to the WordPress environment.
- Supply it through the plugin’s supported configuration mechanism.
- Restrict access to its stored copy and to configuration backups.
Do not commit the key to Git, paste it into support tickets, or leave it in a publicly accessible directory. If it leaks, revoke it and issue a replacement.
Where your hosting environment and plugin version support credentials supplied by the runtime, evaluate that option instead of maintaining a downloaded key. Do not assume every 2019 plugin release supports it.
3. Configure WP-Stateless and Test an Upload
Install and activate WP-Stateless from the WordPress plugin directory. In its settings, connect the Google Cloud project, service-account credentials, and bucket.
WP-Stateless releases offer different operating modes. Broadly, these range from copying uploads to storage while retaining local files to serving offloaded media and removing local copies. Read the descriptions in your installed release rather than assuming the mode names tell the whole story.
Start with a mode that preserves local files. Then:
- Upload a new image through the Media Library.
- Confirm the original appears in the bucket.
- Confirm WordPress-generated image sizes are uploaded as well.
- Open a page containing that image and inspect its URL.
- Verify that the image loads while logged out.
Check responsive images too. An image can have a correct src while its srcset still references local thumbnails or inaccessible objects.
Migrate the Existing Media Library
Use the plugin’s synchronization tools to copy existing attachments and their generated sizes. Work in batches if the library is large, and watch PHP execution limits, memory use, and failed uploads.
Existing post HTML, custom fields, page builders, and CSS can contain hard-coded upload URLs that are not automatically rewritten. Inspect representative pages before considering a database replacement. If replacement is necessary, use a serialization-aware tool and take a fresh backup first.
Keep local uploads until migration and rollback testing are complete.
4. Add Cloud CDN in Front of the Bucket
Cloud CDN uses Google’s external HTTP(S) load-balancing infrastructure. For Cloud Storage media, the origin is a backend bucket, not a backend service pointing at your WordPress server.
In the load-balancing configuration:
- Create a backend bucket referencing your media bucket.
- Enable Cloud CDN for that backend bucket.
- Configure the URL map to route media requests to it.
- Create the frontend configuration and reserve an external IP address.
- Configure HTTPS with a certificate covering
media.example.com. - Point the hostname’s DNS record at the frontend IP address.
Google’s console terminology and certificate workflows vary, so validate each resource before proceeding. HTTPS must work for the media hostname before you put it on an HTTPS WordPress site.
A DNS alias to a Cloud Storage hostname alone does not create this load-balancer-and-CDN setup or provision the necessary HTTPS certificate.
Preserve Object Paths
Configure WP-Stateless’s supported custom-domain setting to use your CDN hostname. The exact field label depends on the release.
If an object is stored as:
2019/08/header-image.jpg
then the corresponding CDN URL should normally be:
https://media.example.com/2019/08/header-image.jpg
Check any configured directory prefixes. Accidentally adding the bucket name or duplicating uploads in the URL is a common cause of 404 responses.
5. Set and Verify Cache Behavior
Cloud CDN caching depends on response properties and cacheability rules. Set appropriate Cache-Control metadata on your storage objects rather than assuming that enabling the CDN caches every response indefinitely.
For public media that may occasionally be replaced, a starting policy could be:
Cache-Control: public, max-age=3600
For versioned filenames that never change, longer lifetimes can be appropriate. Avoid a long cache lifetime for files you routinely overwrite at the same URL.
Changing plugin defaults may affect only future uploads. Check existing object metadata separately.
Inspect a delivered object:
curl -I https://media.example.com/2019/08/header-image.jpg
Look for a successful status, the correct Content-Type, and your intended cache policy. Repeated requests may show an Age header, but use load-balancer logging and CDN monitoring to investigate cache behavior rather than treating one header as definitive proof.
When replacing media, a new filename is usually simpler than invalidation. CDN invalidation does not remove copies already cached in visitors’ browsers.
6. Consume Media Through the WordPress REST API
The WordPress REST API is built into current WordPress releases in 2019. You do not need another plugin to read public posts and public attachment records.
Request recent posts with embedded related resources:
curl 'https://www.example.com/wp-json/wp/v2/posts?per_page=5&_embed=1'
For a specific attachment:
curl 'https://www.example.com/wp-json/wp/v2/media/123'
The media response includes source_url and, for images, generated sizes under media_details.sizes. With offloading and URL rewriting working correctly, these should reference your intended media delivery domain.
A simple browser client can display an attachment without downloading the image through WordPress:
async function showMedia(id, container) {
const response = await fetch(
`https://www.example.com/wp-json/wp/v2/media/${id}`
);
if (!response.ok) {
throw new Error(`Media request failed: ${response.status}`);
}
const media = await response.json();
const image = document.createElement('img');
image.src = media.source_url;
image.alt = media.alt_text || '';
container.appendChild(image);
}
If API responses still contain local URLs, inspect the installed plugin’s REST compatibility and URL filters. Do not blindly replace domain strings throughout JSON.
Keep Writes Authenticated
Creating posts or uploading media through POST /wp-json/wp/v2/media requires authentication and the appropriate WordPress capabilities.
For code running within WordPress, cookie authentication with a valid REST nonce is the normal approach. The nonce accompanies the logged-in session; it is not a standalone password.
External clients need a deliberately selected authentication solution compatible with your 2019 installation. Application Passwords are not yet a WordPress core feature in this timeframe. Never expose administrator credentials or the Google service-account key in frontend JavaScript.
REST uploads still pass through WordPress. Test that your plugin offloads them just as it offloads Media Library uploads.
Common Pitfalls and Final Checks
Before switching off local media storage, verify these failure points:
- 403 responses: Check object readability, service-account permissions, and ACL compatibility.
- 404 responses: Compare the requested path with the actual object key.
- Mixed content: Ensure all rewritten URLs use the working HTTPS media hostname.
- Missing thumbnails: Confirm every required image size was synchronized.
- Broken editing workflows: Test cropping, thumbnail regeneration, backups, and optimization plugins without local originals.
- Unexpected CORS errors: Distinguish cross-origin API requests from media requests. Ordinary image display usually needs no storage CORS policy; JavaScript pixel access and some other asset uses do.
Also test attachment deletion: removing a WordPress record and removing its cloud objects may depend on plugin settings. Monitor storage, request, network, and load-balancing charges; offloading is not a guarantee of lower total cost.
Takeaway
Treat this integration as three connected jobs: offload media safely, configure a real CDN delivery path, and verify the URLs exposed through the REST API. Once each works independently, you have a more portable media layer and an API-friendly publishing system, without confusing media statelessness with a fully stateless WordPress application.
Conversation (0)
No comments yet. Start the conversation.
Join the conversation
Create a free member account to comment.