Wiring Claude Code Into a Hostinger WordPress Site: the Honest Playbook

For about an hour, the whole thing was dead.

Every attempt to publish a post came back with the same error: 401 not logged in. The username was right. The password was valid. I checked both three times. And the setup still refused to work.

The fix, when it finally came, wasn’t a smarter AI model or a cleverer script. It was SSH. The moment I gave the agent real eyes on the server, the actual bug showed itself in about ninety seconds, and it turned out I’d been chasing the wrong villain the entire time.

This is the honest version of that setup. What I was building, where it broke, the detour through three different AI models, and the handful of practices I’d keep for any WordPress site on Hostinger. If you want your terminal AI to draft and publish posts for you, this is the path that actually worked, including the parts I got wrong.

What I was trying to build

The goal was simple to say and annoying to do. I wanted Claude Code, Anthropic’s agentic coding tool that lives in your terminal, to take a finished blog draft and push it into WordPress as a draft post. Not publish it. Just get it into the site, formatted and ready, so I could add a featured image and hit Publish myself.

WordPress has a built-in REST API for exactly this. A REST API is just a set of web addresses a program can call to read and change your site’s data instead of clicking around the admin screens. WordPress also supports Application Passwords, which are revocable API keys tied to one user. You create one, a script uses it to authenticate, and you can kill it later without touching your real login. That’s the whole foundation.

The fast path: REST plus an application password

If you’re on Hostinger’s Premium plan or higher, most of the pieces are already there. Here’s the part worth checking first, because it saved me an install I assumed I’d need.

Most managed WordPress hosts pre-install a command-line tool called wp-cli. It’s a separate project, not part of WordPress itself, but you rarely have to install it by hand. On my Hostinger plan it was already there. One SSH command confirmed it:

wp --info

That printed WP-CLI 2.12.0. If yours comes back empty, you can grab it directly:

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar && mv wp-cli.phar ~/bin/wp

For the publishing script itself, the fast path is REST plus an application password. The key rule, and the thing most tutorials get wrong: the password never lives in your code. It lives in an environment file that git ignores, and the script reads it from there.

import os, requests

WP_URL = os.environ["WP_URL"]                     # e.g. https://yourdomain.com
AUTH   = (os.environ["WP_USER"], os.environ["WP_APP_PASSWORD"])

def create_draft(title, html):
    return requests.post(
        f"{WP_URL}/wp-json/wp/v2/posts",
        json={"title": title, "content": html, "status": "draft"},  # draft only
        auth=AUTH,
        timeout=30,
    ).json()

Notice status is hardcoded to draft. That’s deliberate, and I’ll come back to why it matters. Before wiring any of this into an agent, test the connection with one line of curl:

curl -u "your-username:your-application-password" https://yourdomain.com/wp-json/wp/v2/users/me

If that returns your user details, you’re connected. If it returns a 401, welcome to my afternoon.

Then it broke, and stayed broken

My 401 wouldn’t budge. And the error was a liar.

The first thing you find when you search a WordPress 401 on managed hosting is header stripping. Some hosts and their content delivery networks (a CDN is the caching layer that sits between visitors and your server) drop the Authorization header before it reaches WordPress, so your password never arrives. It’s a real problem, it fit my symptoms, and I spent a good while convinced it was the answer.

It wasn’t. But I didn’t know that yet, and this is where the story gets honest.

The detour: three AI models and one aha

I started this work on Fable 5, one of the newer reasoning-heavy models. For a plumbing problem like a stubborn 401, that was the wrong tool. It kept analyzing, kept theorizing, kept generating more possibilities to consider, and the credits drained fast while the site stayed broken. In my own words at the time: Fable was eating way too many credits too fast overthinking the task, and I actually ended up using Grok for the steps where the aha moment was the SSH setup, then walked Claude through the steps.

That’s worth sitting with, because it’s the real lesson under the technical one. A model that reasons harder is not automatically better. On an operations task, where the answer is “go look at the actual server,” heavy reasoning is just an expensive way to guess. Grok got me to the aha faster: stop theorizing, turn on SSH. Then I switched Claude to Opus 4.8, which is quick and direct on hands-on work, and drove it through the fix.

Three models in one afternoon isn’t a failure. It’s what using AI actually looks like when you’re being honest about it. You reach for whatever gets you unstuck.

What SSH actually unlocked

SSH is just a secure way to open a terminal directly on your server. Hostinger has it, and turning it on is a toggle plus adding a key. The instant I had it, the whole problem changed shape, because now the agent could inspect reality instead of guessing at it.

Three things became possible that the REST API alone could never show me:

First, I could read the site’s actual .htaccess file, the config that controls how requests get rewritten, and confirm the auth-passthrough rule was already in place. So header stripping at that layer was ruled out, not assumed.

Second, and this was the real move, I could test the REST API from inside the server, bypassing the CDN entirely by pointing the request at localhost. Same request, same fake credentials, run once from the public internet and once from the machine itself.

Third, I could run wp-cli, which meant I could ask WordPress directly who my user even was.

The real bug, and it was embarrassing

Here’s what those inside-the-server tests proved. A quick check confirmed the authorization header was reaching the server intact, with my username attached, whether the request came from localhost or through the public CDN. So nothing was stripping it. WordPress had been seeing my credentials all along. The CDN was innocent.

So if the password was reaching WordPress and still failing, the problem was the password or the username. And it was the username.

I’d been authenticating with the site’s display name. WordPress wanted the account’s actual login slug, which was different. That’s it. That’s the hour. Not a CDN, not header stripping, not a config nightmare. One wrong field, in a form nobody makes you double-check, hidden behind an error message that says “not logged in” instead of “wrong username.”

I only found it because I could run one command on the server:

wp user list --fields=ID,user_login,roles

The login staring back at me was not the name I’d been typing. Fixed the field, reran the curl test, and it authenticated on the first try.

Locking it down: the setup I actually trust

Getting it working is step one. Getting it working safely is what makes it something you’d leave running. A few practices earned their place today.

Publish as a draft, always. The script only ever creates drafts, and a human hits Publish in WordPress. Reach is hard to reverse. A wrong post that sits in draft is a shrug; a wrong post that goes live and gets indexed is a bad afternoon. Hardcoding status="draft" costs nothing and removes the whole category of accident.

Give the agent the smallest account that does the job. My first working version authenticated as an administrator, which was more power than a publishing script has any business holding. So I made a new user with the Editor role, gave that user its own application password, and revoked the admin one. Now, if that credential ever leaks, it can write posts. It cannot touch plugins, settings, or other users.

Keep secrets out of chat and out of code. The application password never got pasted into a conversation and never got written into a script. It lives in one gitignored file that only the script reads. When you’re working with an AI agent, this matters double, because anything you paste into the chat is now sitting in a transcript.

Match the model to the task. Reasoning-heavy models for genuinely hard problems. Fast, direct models for operational work. The afternoon would’ve been shorter and cheaper if I’d started there.

What REST still can’t do

REST plus wp-cli covers most of what you’ll want, but not everything, and it’s worth knowing the edges before you promise yourself full automation.

Reading and writing posts, pages, and media is clean over REST. Most SEO plugins, though, keep their settings behind their own systems. Our site runs AIOSEO, which does expose its own REST namespace, so some of that is reachable, but the deeper actions like regenerating a sitemap often want wp-cli or a click in the admin. Cache clears and plugin-specific settings tend to live in the same bucket.

When REST won’t reach something, you have two honest options. Run it through wp-cli over SSH, which can touch almost anything WordPress stores. Or, for the handful of actions that only exist as a button in the admin, drive the browser with a tool like Playwright and actually click it. Neither is elegant. Both work.

A few questions people asked me about this

Do I need to know how to code to connect Claude Code to WordPress?

Not much. The setup is a short Python file and a few terminal commands, and the AI writes most of it alongside you. If you can follow a recipe and paste a command into a terminal, you can get this running. The hard part isn’t the code. It’s giving the agent the right access, which is more of a settings problem than a coding one.

Is it safe to let an AI agent publish to my live site?

It is, if you scope it down. Give the agent its own WordPress user with an Editor role, not an administrator, and its own application password you can revoke anytime. Have it create drafts by default, so a person approves anything before it goes public. The risk was never the AI. It’s handing any tool more power than the job needs.

The WordPress REST API keeps returning a 401 error. What’s wrong?

Check the username before you touch the password. WordPress wants the account’s login name, which is often different from the display name you see in the dashboard, and the error reads “not logged in” either way. If the username is right and it still fails, confirm your host isn’t stripping the authorization header before it reaches WordPress. Those two account for most 401s.

Should I use a ready-made publishing tool or build my own?

Build your own, if the job is close to your real work. A prebuilt integration is faster to start, but it fits a generic version of the task, and you’ll spend more time fighting its assumptions than you saved. I’ve written before about why copying an AI setup rarely fits, and building your own keeps you valuable. The same logic holds here. The setup that matches how you actually publish is the one worth owning.

The thing I’d tell myself an hour earlier

The whole afternoon turned on one decision, and it wasn’t which AI model to use.

It was giving the agent real access to the system it was working on. As long as it could only see the site from the outside, through a REST API and a lying error message, it could theorize forever and get nowhere. The instant it could open a terminal on the actual server, the truth took ninety seconds. Better reasoning didn’t solve this. Better visibility did.

If you’re wiring an AI agent into infrastructure you own, that’s the move. Not a smarter prompt. Not a more powerful model. Access. Let it see the machine, and most of the mystery stops being mysterious.