One question before every door.
Your server asks before a login, signup, checkout or form post. Ropeline answers in plain words.
For developers · any platform
Not on WordPress? Your site still gets card testing, fake signups, password guessing and form spam. Ask Ropeline before each login, signup, checkout or form post, and it tells you to allow, challenge or block it.
Your server asks before a login, signup, checkout or form post. Ropeline answers in plain words.
With the reasons, so you always know why.
If Ropeline doesn't answer within 1.5 seconds, the code on this page lets the visitor through.
Bots that other sites stopped are challenged at yours before they try a single password.
A Worker in front of your site asks Ropeline for you. Your site's code doesn't change.
Ropeline only needs the visitor's address and what they tried.
Bots don't check what your site is built on. They try every login, every signup and every checkout they can find. Ropeline now stands at all of them, on any website, in any language.
Every answer comes with its reasons in plain words, the tries counted so far and the lines for that door.
Nothing unusual. Carry on as normal.
Ask first: show a CAPTCHA, or send a code. Real people pass and carry on.
Turn them away, and your site's report goes to the Ropeline Network's shared list.
From the same signals Ropeline Pro uses on WordPress: how often this address tried on your site in the last hour, the Ropeline Network's shared list, and throwaway email addresses on signups.
Counts failed logins
Challenge from 5Block from 10
Counts signups
Challenge from 3Block from 6
Counts declined payments
Challenge from 3Block from 5
Counts form posts
Challenge from 5Block from 10
Each per address, per website, in an hour. Failed logins and declined payments count when you tell Ropeline they failed; every signup and form post counts when you ask. An address on the shared list is challenged, and blocked once it also reaches the challenge line.
The shared list holds addresses that sites on at least three different networks stopped in the last day. A bot that attacks one site is already known at the next. When an address reaches a block line on your site, Ropeline adds your site's report to the shared list, so your attackers are known everywhere else too.
Send your key in the X-Ropeline-Key header, from your server only: the key is a password, so never put it in a web page.
curl https://network.getropeline.com/v1/check \
-H "X-Ropeline-Key: RL-XXXXX-XXXXX-XXXXX-XXXXX" \
-H "Content-Type: application/json" \
-d '{"site": "example.com", "event": "login", "ip": "203.0.113.7"}'
import { createRopeline } from "./ropeline.js";
const ropeline = createRopeline({ key: process.env.ROPELINE_KEY, site: "example.com" });
app.post("/login", async (req, res) => {
const verdict = await ropeline.check("login", { ip: req.ip }); // behind a proxy: app.set("trust proxy", true)
if (verdict.action === "block") return res.status(429).send("Too many tries. Please wait an hour.");
if (verdict.action === "challenge" && !captchaPassed(req)) return showCaptcha(res);
if (!(await passwordIsRight(req.body))) {
await ropeline.failed("login", { ip: req.ip });
return res.status(401).send("Wrong email or password.");
}
// ...log them in
});
function ropeline_check($event, $ip, $failed = false, $email = '') {
$ch = curl_init('https://network.getropeline.com/v1/check');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT_MS => 1500,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-Ropeline-Key: ' . getenv('ROPELINE_KEY')],
CURLOPT_POSTFIELDS => json_encode(['site' => 'example.com', 'event' => $event, 'ip' => $ip, 'failed' => $failed, 'email' => $email]),
]);
$answer = json_decode((string) curl_exec($ch), true);
return is_array($answer) && isset($answer['action']) ? $answer['action'] : 'allow';
}
if (ropeline_check('signup', $_SERVER['REMOTE_ADDR'], false, $_POST['email']) === 'block') {
http_response_code(429);
exit('Too many signups from your connection. Please try again later.');
}
import os, requests
def ropeline_check(event, ip, failed=False, email=""):
try:
r = requests.post("https://network.getropeline.com/v1/check", timeout=1.5,
headers={"X-Ropeline-Key": os.environ["ROPELINE_KEY"]},
json={"site": "example.com", "event": event, "ip": ip, "failed": failed, "email": email})
return r.json().get("action", "allow")
except requests.RequestException:
return "allow"
# After a declined payment (card testing shows up as many of these from one address):
ropeline_check("checkout", request.remote_addr, failed=True)
{"action": "block", "reasons": ["10 failed logins from this address on this site in the last hour"],
"listed": false, "tries": 10, "limits": {"challenge": 5, "block": 10}, "plan": "site"}
Trying it out? Send "site": "localhost" first: test copies of a site (localhost, and addresses ending in .local, .test or .localhost) never take one of your plan's places.
POST https://network.getropeline.com/v1/check, with your key in the X-Ropeline-Key header and a JSON body.
example.com (or example.com/shop for a site in a folder). Each website takes one of your plan's places (local copies such as localhost don't).login, signup, checkout or form.X-Forwarded-For header (on Cloudflare, CF-Connecting-IP). A private address such as 10.x or 192.168.x is a proxy, not a visitor: it's let through with a warning and never counted, so a setup mistake can't lock everyone out.true after a wrong password or a declined payment, so it counts.action is allow, challenge or block, with the reasons in plain words. It may also carry a warning (something to fix in how you call it) or a note (such as checkout protection being in the store plans). A wrong key or event gets an error and HTTP 4xx; a plan that ended answers allow, so your site keeps working.Site behind Cloudflare? Put ropeline-worker.js in front of it. It asks Ropeline about each POST to the paths you list, answers the ones to block with "429 Too many tries", passes the rest on with an X-Ropeline-Action header, and tells Ropeline about the logins and payments your site turned down (from the status your site answers with).
In Cloudflare, go to Workers & Pages, Create, Start with Hello World, and paste the file in place of the example.
Under Settings, Variables and Secrets, add ROPELINE_KEY (as a secret), ROPELINE_SITE (example.com) and ROPELINE_PATHS, such as {"/login": "login", "/signup": "signup", "/checkout": "checkout", "/contact": "form"}.
Under Settings, Domains and Routes, add a route: example.com/*.
A plugin. No code.
One API call. Any language.
A Worker. No code changes.
Any Ropeline key works, on WordPress or anywhere else. Try any plan free for a week with just your email address: no card.
$5 a month
One website
From $15 a month
One online store, sized by paid orders a month
$79 a month
Up to 50 websites
The Site plan covers logins, signups and forms on one website; the Store plans and Agency add checkout (card testing). Store plans are sized by orders a month, and Agency covers up to 50 websites, and Agency 10 up to 10 for $29 a month. See every plan.
The tries are counted under a scrambled copy of each visitor's address (mixed with your key and website), kept for 2 hours.
An address is only kept as it is when it reaches a block line, as your site's report to the shared list, for 7 days.
Email addresses are checked against the list of throwaway services and never kept. Ropeline never asks for passwords or card numbers.
More in the privacy policy.
No. Anything that can send one HTTPS request from your server works: Node, Deno, Bun, PHP, Python, Ruby, Go, Java or .NET. Or put a Cloudflare Worker in front of your site and change no code at all. On WordPress or WooCommerce, the plugin does all of this and more, with no code.
Your visitors get through. The code on this page and the Worker wait at most 1.5 seconds, then let the visitor in. Ropeline should never be why a real customer can't log in or pay.
Ask the visitor to prove they're human before you carry on: a CAPTCHA such as Cloudflare Turnstile, or a code by email. Real people pass and carry on.
Yes. getropeline.com isn't WordPress, so it uses Ropeline for any website, with the same code as this page: our checkout, our free-week form and our dashboard sign-in ask Ropeline first. A blocked connection gets no card form. A challenged one is sent to the free week instead, where the link we email is the challenge.
No, from your server only. The key is a password, and a bot controls everything that runs in its own browser.
Not if you test with "site": "localhost": test copies of a site (localhost, and addresses ending in .local, .test or .localhost) never take one of your plan's places. Each real website does.
Not yet. Those builders run their own logins and checkouts, with no place for your code to ask Ropeline first.