| 开发者 | gallopsoftware |
|---|---|
| 更新时间 | 2026年10月15日 04:31 |
| PHP版本: | 8.1 及以上 |
| WordPress版本: | 7.1 |
| 版权: | GPLv2 or later |
| 版权网址: | 版权信息 |
The WordPress editing experience your team already knows, with the speed of a fully decoupled Next.js front end. One endpoint, one response, done.Visit the Gallop homepage & documentation → https://www.youtube.com/watch?v=G-xBcAS8ux4 Most headless setups make you stitch together a waterfall of core WordPress REST calls per page —
/wp/v2/posts, /wp/v2/media, meta, and taxonomy — then bolt on a JWT layer and an auth service just to log a user in. Gallop replaces all of that.
One request. The whole page. Hand Gallop a URI and it returns the post body, an SEO block, and your global site data — already joined, already resolved, ready to render. The API is the point: a dedicated, Next.js-shaped REST namespace (/wp-json/gallop/v1) so your front-end code stays simple — one fetch, one response, ready to render.
Why choose Gallop?
post, seo, and site — in a single response.seo block ships search-ready out of the box.register_post_type() boilerplate.post, its seo metadata, and your global site data, already joined, already resolved, ready to render. GET|POST /gallop/v1/post handles posts and pages, and POST /gallop/v1/category does the same for taxonomy archives. No waterfall of /wp/v2/posts, /wp/v2/media, meta, and taxonomy calls per page — one round trip instead of five, with no JWT, API keys, or complicated authentication to set up. Your front end stays simple, and your pages load fast.
The SEO is done for you. With Yoast active, the seo block is populated straight from Yoast's indexables (canonical, meta description, OpenGraph, robots flags, reading time) so every page ships search-ready out of the box. Without Yoast, seo comes back as an empty object instead of disappearing, so your front end can check it and fall back to its own defaults.
Login, members, and comments, already wired up
https://vimeo.com/1200535505
Moving off a WordPress template usually means rebuilding everything it gave you for free. Gallop ships with it already done. Readers log in, sign up, reset a password, and edit their profile on your Next.js site, and every one of them is an ordinary WordPress user. Editors sign in with their normal WordPress credentials. There is no JWT layer and no separate auth service to stand up: your front end's server talks to WordPress with one key, and keeps its own session for each member.
Comments stay in WordPress too. Visitors and members comment on your front end; WordPress stores, moderates, and notifies exactly as it does for its own comment form.
Settings and custom post types, configured from WordPress
https://vimeo.com/1196416170
Point Gallop at your Next.js production URL and it 301-redirects public WordPress front-end requests to the matching path on your headless host. Admin, REST API, and previews are left untouched.
Register REST-enabled custom post types from the Post Types tab and they're immediately available through the Gallop namespace — no register_post_type() boilerplate, no developer round trip. Core post types are left alone, and content you create survives a deactivate/uninstall.
Trusted on real production sites
Gallop isn't a proof of concept — it powers live production sites today:
gallop/v1 namespace.
GET|POST /gallop/v1/post — Resolve a post and return post, seo, and site payloads. Accepts uri, id, or slug with type.GET|POST /gallop/v1/posts — A filtered, paged collection of posts of one type. Accepts category, include, meta_key / meta_value / meta_compare, orderby, order, size, offset, and fields.GET|POST /gallop/v1/posts/list — Slug, uri, and modified date for every published post of a type, for sitemaps and static builds. Optional parent.POST /gallop/v1/category — Resolve a category URI to a term and return category, seo, and site payloads.GET /gallop/v1/comments — The approved comments on a post, oldest first, as a flat list with parent ids. Public.POST /gallop/v1/comments — Submit a visitor's or a member's comment. Requires the API key.POST /gallop/v1/members/login, POST /gallop/v1/members, POST /gallop/v1/members/confirm, POST /gallop/v1/members/create, POST /gallop/v1/members/reset-request, POST /gallop/v1/members/reset, GET|PATCH /gallop/v1/members/{id} — Members: log in, sign up, confirm an address, create an account the front end confirmed, reset a password, read and edit a profile. All require the API key.POST /gallop/v1/auth/login, POST /gallop/v1/auth/logout, GET /gallop/v1/auth/session — The older cookie-based login, kept for front ends on the same registered domain that use it.wp_authenticate() as the visitor, so login-protection plugins see the visitor's address. No WordPress cookies are set.sessionVersion handed back with each request./members/create, /members/reset, or a profile change, must be 8 to 128 characters. Logging in accepts whatever password the account already has.
Five wrong passwords for one login name from one address within fifteen minutes return HTTP 429; each address is sent at most one email every ten minutes and five a day; login, sign-up and reset answer the same whether or not the address is known.
Comments
Show WordPress comments on a headless front end, and let visitors leave them, without giving up anything WordPress does with a comment.
GET /gallop/v1/comments?post=<id> returns the post's approved comments, oldest first, each with id, parent, authorName, authorUrl, isPostAuthor, avatar, dateGmt, and rendered content. Build the thread from parent. It also returns open, count, requireNameEmail, threadDepth, and truncated. A commenter's email address, IP address, and browser are never returned.POST /gallop/v1/comments takes post, parent, authorName, authorEmail, authorUrl, content, and the visitor's ip, userAgent, and referer. It answers 201 with the comment, its status (approved, hold, spam, or trash), and the post's new count.wp_handle_comment_submission(), the function WordPress's own comment form uses. Your Discussion settings, moderation, the duplicate and flood checks, anti-spam plugins such as Akismet, and notification emails all behave as they do for a comment left on a WordPress site. Core's own REST route for comments sends no notification emails.
Send the request from your front end's server, never from the browser: it carries the API key.
API key
Reading from Gallop needs no key. Writing does: a request that changes something in WordPress on a visitor's behalf has to prove it comes from your front end's own server.
X-Gallop-WP-Key header, over HTTPS.GALLOP_WP_API_KEY. Never give it a NEXT_PUBLIC_ prefix, and never put it in code.wp-config.php with define( 'GALLOP_WP_API_KEY', 'gallopwp_…' );. A key set there takes priority over a generated one.seo block in the post and category responses is populated from Yoast's indexable data (canonical, meta description, OpenGraph fields, robots flags, reading time, etc.). Without Yoast, seo is returned as an empty object so clients can branch safely.
Data stored
gallop_post_types (option) — your custom post type definitions.gallop_nextjs_production_url (option) — the redirect target, if configured.gallop_trust_forwarded_ip (option) — whether to trust reverse-proxy IP headers when rate-limiting auth (default off).gallop_api_key_hash (option) — a hash of your API key, with the date it was generated and its last four characters. Never the key itself.gallop_api_key_permissions (option) — what the key is allowed to do.gallop_verified, gallop_subscribed, gallop_reply_emails, gallop_session_version (user meta) — whether a member's address is waiting for confirmation, their email choices, and their session version. _gallop_reply_notified (comment meta) — a reply already emailed about.gallop_auth_* (transients) — short-lived login rate-limit counters.gallop_pending_* (transients) — sign-ups awaiting confirmation, up to 48 hours: address, names, and a hash of the link's key.gallop_confirm_sent_* (transients) — when each address was last emailed, for a day.gallop_key_fail_* (transients) — short-lived counters of wrong API keys, by address.gallop_key_reveal_* (transient) — a newly generated key, held for up to two minutes so it can be shown once after the page reloads, then deleted.gallop folder to /wp-content/plugins/, or install the ZIP from the Plugins screen.https://your-wp-site.example/wp-json/gallop/v1 and start fetching the post and category endpoints.No. Gallop's REST endpoints are framework-agnostic JSON. Next.js is the reference target and the redirect feature is named for it, but any HTTP client can consume the API.
No. The redirect runs on template_redirect only, skips any request with a preview=true or _wp* query parameter, and never touches /wp-admin or /wp-json. Leave the Next.js URL setting blank to disable redirection entirely.
The front end's server sends the login name, password, and the visitor's address to /gallop/v1/members/login with the API key. Gallop checks the password with wp_authenticate(), the function behind wp-login.php, and answers with the member's details; the front end then keeps a session of its own for them. No WordPress cookies are involved, so the front end can be on any domain. A two-factor plugin on the WordPress login screen does not apply to front-end logins. The older /gallop/v1/auth/* routes, which set WordPress's own cookies for a front end on the same registered domain, still work.
Nothing is written to the users table. Gallop keeps the address and names for up to 48 hours and emails a confirmation link to the front end. When the link is opened the front end calls /gallop/v1/members/confirm, and only then is a user created, with the Subscriber role. If the address already had an account, it is marked verified (and subscribed, if that was asked) and the person is told to log in; the link never logs anyone into an existing account. WordPress's "Anyone can register" setting is not consulted: the Manage members permission is the switch.
Yes. A front end that confirms the address its own way skips the two routes above and calls POST /gallop/v1/members/create with email, password, and optionally firstName, lastName and subscribe, once the address is confirmed. Gallop creates the user at once, with the Subscriber role, marked verified, with that password, and answers 201 with the member. It sends no email and keeps nothing waiting. An address that already has an account is answered 409 gallop_member_exists and left as it is. Gallop takes the front end's word that the address was confirmed: only call this after it was.
Every refusal has a code. Where WordPress gave a reason of its own, it is in data.reason.
gallop_key_missing (401), gallop_key_invalid (401), gallop_key_forbidden (403, the key lacks Manage members), gallop_https_required (403), gallop_key_rate_limited (429).gallop_member_invalid_credentials (401, the same whether the account is missing or the password is wrong), gallop_member_rate_limited (429).gallop_member_invalid_ip (400, login, sign-up and reset requests need the visitor's ip), gallop_member_invalid_email, gallop_member_invalid_name, gallop_member_weak_password (all 400).gallop_member_link_invalid (400, the confirmation or reset link has expired or was already used).gallop_member_session_invalid (401, the member does not exist or sessionVersion is out of date; ask them to log in again). Commenting as a member can be refused with it too.gallop_member_wrong_password (403, the current password is needed to change the email or password and was not right), gallop_member_email_taken (409), gallop_member_update_failed (400).gallop_member_exists (409), gallop_member_create_failed (500).gallop_member_front_end_missing (500, the Next.js Production URL is not set, so no email can link to the front end).Four, all through wp_mail() and all to the member: a confirmation link, a note that an address already has an account, a password reset link, and, if the member asked for it, a message when someone replies to their comment. Every link points at your Next.js production URL. The gallop_member_email filter can change or suppress any of them.
Yes. Five failed attempts per login name + client IP within fifteen minutes return HTTP 429 until the window expires. Successful logins clear the counter. Login, sign-up, and reset requests also answer the same whether or not the account exists, and take the same time, so they cannot be used to find out who has an account.
By default Gallop uses REMOTE_ADDR for the per-IP portion of the login rate limit. If your site sits behind a trusted reverse proxy (Cloudflare, a load balancer, etc.) that overwrites the client-IP headers, enable Trust proxy IP headers on the Gallop settings screen so CF-Connecting-IP / X-Forwarded-For are used instead. Leave it off on direct-served sites — turning it on without a trusted proxy lets attackers spoof those headers to bypass the rate limit. The setting can also be overridden in code via the gallop_trust_forwarded_ip filter.
Published posts of a public type that are not password protected. Anything else, a missing post included, answers the same 404 gallop_comments_post_not_found, so the route cannot be used to find out that a draft exists. The newest 500 comments are returned by default; truncated is true when there are more, and gallop_comments_max raises the ceiling.
Every refusal has a code, and WordPress's own reason in data.reason.
gallop_key_missing (401), gallop_key_invalid (401), gallop_key_forbidden (403, the key lacks the permission), gallop_https_required (403), gallop_key_rate_limited (429).gallop_comment_closed (403), gallop_comment_login_required (403), gallop_comments_post_not_found (404).gallop_comment_name_email_required, gallop_comment_invalid_email, gallop_comment_empty, gallop_comment_too_long, gallop_comment_name_too_long, gallop_comment_email_too_long, gallop_comment_url_too_long, gallop_comment_invalid_ip (all 400).gallop_comment_invalid_parent, gallop_comment_thread_too_deep, gallop_comment_threading_disabled (all 400). A parent must be an approved comment on the same post.gallop_comment_duplicate (409), gallop_comment_flood (429).gallop_comment_rejected (400, another plugin refused it), gallop_comment_save_failed (500).Your server passes on the visitor's IP address, browser, and the page they commented from. While the comment is being saved, Gallop presents those details to WordPress and to other plugins in place of the server's own, then puts the originals back. That is what lets WordPress record, throttle, and spam-check each comment against the person who wrote it. The details are only accepted from a request whose key has been verified.
The key is only accepted over HTTPS, or in a local environment. If your site is served over HTTPS and you still see this, it is probably behind a proxy that ends TLS without telling WordPress. Fixing is_ssl() in wp-config.php is the right answer, since WordPress's own Application Passwords need it too. The gallop_api_key_transport_secure filter is there if you cannot.
After ten wrong keys from one address in fifteen minutes, further wrong keys from it are answered 429 gallop_key_rate_limited. A correct key is never refused for that reason, so nobody can lock your front end out by sending wrong ones. If the key in wp-config.php is not valid, every request that needs a key is refused and an admin notice says so.
Anyone may read a published post's comments, as on any WordPress site. Submitting one is different: it arrives from your front end's server on a visitor's behalf, carrying the visitor's IP address for WordPress to record and check. The key is how Gallop knows the request, and that address, really came from your server. Without it, anyone could post straight to WordPress, skip whatever spam checks your front end runs, and claim any address they liked.
No. Gallop stores a hash of the key, not the key. Regenerate it under Gallop → Settings → Front-end connection and install the new one. The old key stops working as soon as you do.
That is decided by your Discussion settings, exactly as for a comment left on a WordPress site. The response to a submitted comment reports its status, so your front end can tell a visitor that their comment is waiting for approval.
No. If Yoast is not active the seo field in responses is an empty object. With Yoast active, Gallop reads from its indexables to populate canonical, OpenGraph, and robots data.
No. Posts, pages, media, and built-in taxonomies are left alone. Only post types you create through the Gallop admin screen are registered by this plugin.
Deactivating stops Gallop from registering its post types and REST routes; content created under those post types stays in the database. Deleting the plugin (via the Plugins screen) additionally removes every gallop_* option and transient. Posts authored under your custom post types are intentionally left in place so they survive an uninstall/reinstall. So are comments and member accounts; what Gallop recorded about members (a few user meta rows) is removed.
Actions
gallop_auth_login_success — fires after a successful REST login. Args: WP_User $user, WP_REST_Request $request.gallop_auth_login_failed — fires after a failed REST login. Args: string $username, WP_REST_Request $request.gallop_auth_logout — fires after a REST logout. Args: WP_User $user, WP_REST_Request $request.gallop_comment_submitted — fires after a submitted comment has been saved, whatever its status. Args: WP_Comment $comment, WP_REST_Request $request.gallop_comment_rejected — fires when WordPress refused a submitted comment. Args: WP_Error $error, WP_REST_Request $request.gallop_api_key_generated — fires after a key has been generated. The key is not passed. Args: string $keyId.gallop_api_key_verified — fires when a request presented a valid key for a permission it holds. Args: string $keyId, string $capability, WP_REST_Request $request.gallop_api_key_failed — fires when a request presented a key that is not this site's. Args: WP_REST_Request $request.gallop_member_login — fires after a member logged in through the front end. Args: WP_User $user, WP_REST_Request $request.gallop_member_login_failed — fires after a failed member login. Args: string $login, WP_Error $error, WP_REST_Request $request.gallop_member_signup_requested — fires when someone asked to sign up. The address is not passed: it is not yet known to belong to them. Args: string $ip, WP_REST_Request $request.gallop_member_verified — fires when a member's address is confirmed, or an account is created already confirmed. Args: WP_User $user.gallop_member_registered — fires after an account was created through the front end, by /members/confirm or /members/create. Args: WP_User $user, WP_REST_Request $request.gallop_member_password_reset — fires after a member reset their password. Args: WP_User $user.gallop_member_updated — fires after a member changed their profile. Args: WP_User $user, WP_REST_Request $request.gallop_trust_forwarded_ip — filter the boolean controlling whether reverse-proxy IP headers (CF-Connecting-IP, X-Forwarded-For) are trusted when rate-limiting REST auth. Defaults to the "Trust proxy IP headers" setting. Only enable behind a trusted proxy that overwrites these headers, otherwise the per-IP rate limit can be bypassed by spoofing them.gallop_resolved_post — the post a /gallop/v1/post request resolved to, before its payload is built. Return another WP_Post to serve that one instead.gallop_pre_post_data — return an array to replace the post payload entirely; gallop_post_data still runs on it. gallop_post_data, gallop_seo_data, gallop_site_data — filter the post, seo and site payloads. Each is passed the WP_Post.gallop_category_data, gallop_category_seo_data, gallop_category_site_data — the same for /gallop/v1/category. Each is passed the WP_Term.gallop_post_type_queryable — whether a post type can be read by id, slug, or listing. Default: public types registered with show_in_rest.gallop_meta_key_queryable — whether /gallop/v1/posts may filter on a meta key. Default: keys registered for the type with show_in_rest. gallop_meta_comparisons — the accepted meta_compare values.gallop_posts_page_max — the most posts one /gallop/v1/posts request returns. Default no ceiling. gallop_posts_list_max — the most /gallop/v1/posts/list returns. Default 5,000.gallop_member_data — filter a member as returned to the front end. Args: array $data, WP_User $user.gallop_member_email — filter an email before it is sent to a member, or return false to send nothing. Args: array $email (to, subject, message, headers), string $kind (confirm, exists, reset or reply), array $context.gallop_comment_data — filter one comment as it is returned. gallop_comments_data — filter the whole response to a request for a post's comments.gallop_comments_max — the most comments one request returns. Default 500.gallop_comment_avatar_sizes — the avatar sizes returned with each comment, in pixels. Default 48 and 96.gallop_comments_cache_control — the Cache-Control header sent with a post's comments.gallop_api_key_capabilities — the permissions a key can be granted. Adding one makes it available to tick; it grants it to nothing.gallop_api_key_max_failed_attempts — how many wrong keys one address may present in fifteen minutes. Default 10.gallop_api_key_transport_secure — whether a request arrived over a connection safe to carry the key.POST /gallop/v1/members/create, for a front end that sends the confirmation email itself: it creates a verified member with the password they chose, and sends no email. Needs the API key with the Manage members permission. An address that already has an account is answered 409 gallop_member_exists.POST /gallop/v1/members and /members/confirm works as before./gallop/v1/members/login and /gallop/v1/auth/login. Passwords are now checked and stored the way WordPress's own login screen does it. A password already set through the reset or profile routes keeps working.GET /gallop/v1/members, POST /gallop/v1/members/{id}/subscription, the "Send mailings" permission, and the email field in the confirm response. A front end that sends mailings keeps its own list of subscribers.POST /gallop/v1/members/login, POST /gallop/v1/members (sign up), /members/confirm, /members/reset-request, /members/reset, and GET|PATCH /members/{id}. A front end's server can let readers log in with wp_authenticate(), sign up with email confirmation, reset a password with WordPress's own keys, and edit a profile, with no WordPress cookies involved. All need the API key with the new "Manage members" permission, which starts off.POST /gallop/v1/comments takes user and sessionVersion, and GET reports openToMembers.gallop_member_data, gallop_member_email. New actions: gallop_member_login, gallop_member_login_failed, gallop_member_signup_requested, gallop_member_verified, gallop_member_registered, gallop_member_password_reset, gallop_member_updated.GET /gallop/v1/comments gains one field./gallop/v1/comments. GET returns a post's approved comments as a flat list with parent ids, ready to thread. POST submits a visitor's comment through WordPress's own comment form handling, so Discussion settings, moderation, the duplicate and flood checks, anti-spam plugins, and notification emails all behave as they do for a comment left on a WordPress site.GALLOP_WP_API_KEY in wp-config.php. Only a hash is stored. It is sent in the X-Gallop-WP-Key header, over HTTPS only.gallop_comment_data, gallop_comments_data, gallop_comments_max, gallop_comment_avatar_sizes, gallop_comments_cache_control, gallop_api_key_capabilities, gallop_api_key_max_failed_attempts, gallop_api_key_transport_secure. New actions: gallop_comment_submitted, gallop_comment_rejected, gallop_api_key_generated, gallop_api_key_verified, gallop_api_key_failed./gallop/v1/posts for filtered collections: post type, category, id list, meta filtering, ordering, paging, and field selection. Defaults to 10 per page; a site can set a ceiling with gallop_posts_page_max. The response reports the size and offset actually used./gallop/v1/posts/list, a lightweight index of every published post of a type, for sitemaps and static builds. Capped at 5,000 by default (gallop_posts_list_max) and reports truncated when the cap is reached./gallop/v1/post can now resolve by id, or by slug together with type, as well as by uri. uri lookups behave exactly as before.post payload gains uri, link, excerpt, featuredImage, author, and categories. Existing fields are unchanged./gallop/v1/auth/login and /auth/session now also return a wp_rest nonce, so a browser session can make cookie-authenticated REST requests.gallop_pre_post_data, gallop_post_data, gallop_seo_data, gallop_site_data, gallop_category_data, gallop_category_seo_data, gallop_category_site_data, gallop_resolved_post.show_in_rest can be read by id, slug, or listing, and only meta keys registered for that type with show_in_rest can be filtered on. This matches core REST behaviour. Widen either with gallop_post_type_queryable or gallop_meta_key_queryable. Accepted meta comparisons can be changed with gallop_meta_comparisons.do_blocks() twice, which had let wpautop add stray <p> and <br> tags./gallop/v1/post and /gallop/v1/category endpoints with optional Yoast SEO payloads./auth/login, /auth/logout, /auth/session) with per-username/IP rate limiting.