<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: xusteve</title>
    <description>The latest articles on DEV Community by xusteve (@xusteve).</description>
    <link>https://dev.to/xusteve</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F481489%2F89f98615-5d4e-45a4-b5dd-42c64828df6e.jpeg</url>
      <title>DEV Community: xusteve</title>
      <link>https://dev.to/xusteve</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xusteve"/>
    <language>en</language>
    <item>
      <title>Giving AI Agents an Email Deliverability Check with MCP</title>
      <dc:creator>xusteve</dc:creator>
      <pubDate>Mon, 02 Nov 2026 06:58:15 +0000</pubDate>
      <link>https://dev.to/xusteve/giving-ai-agents-an-email-deliverability-check-with-mcp-5en7</link>
      <guid>https://dev.to/xusteve/giving-ai-agents-an-email-deliverability-check-with-mcp-5en7</guid>
      <description>&lt;p&gt;Your agent can draft an email in seconds. It still cannot tell whether the sending domain is configured correctly.&lt;/p&gt;

&lt;p&gt;That gap matters. An agent can produce a perfect cold email, newsletter, or support reply, then hand it to a domain with no SPF record, a broken DKIM selector, or a DMARC policy that tells receivers to do nothing. The writing is fine; the infrastructure is not.&lt;/p&gt;

&lt;p&gt;I built a small remote MCP server to give agents a read-only email-domain health check. This post shows how it works, how to connect it, and where I deliberately kept the scope narrow.&lt;/p&gt;

&lt;h2&gt;
  
  
  One tool: &lt;code&gt;scan_domain&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The MCP server is available at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://mcp.mailers.email/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It exposes a single tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scan_domain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Run a domain email health scan (cheap checks; no blacklist)."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputSchema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A call returns checks for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MX records&lt;/li&gt;
&lt;li&gt;SPF&lt;/li&gt;
&lt;li&gt;DKIM&lt;/li&gt;
&lt;li&gt;DMARC&lt;/li&gt;
&lt;li&gt;reverse DNS / PTR&lt;/li&gt;
&lt;li&gt;domain age&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The response includes a score, &lt;code&gt;maxScore&lt;/code&gt;, grade, confidence, individual check details and a &lt;code&gt;fixes&lt;/code&gt; array ordered by what to fix first.&lt;/p&gt;

&lt;p&gt;The public MCP tool intentionally does &lt;strong&gt;not&lt;/strong&gt; run DNSBL blacklist checks. Those are 32+ extra DNS queries per domain and belong in the full web scan at &lt;a href="https://mailers.email/scan/" rel="noopener noreferrer"&gt;mailers.email/scan&lt;/a&gt;, not in a quick agent tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect an MCP client
&lt;/h2&gt;

&lt;p&gt;Add the server to your MCP configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mailers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.mailers.email/mcp"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No API key, account, or OAuth flow is required. The server is anonymous and rate-limited by IP.&lt;/p&gt;

&lt;p&gt;After connecting, ask your agent something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check example.com's email setup. Tell me the three most important fixes and why they matter.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A useful agent response should separate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What is broken&lt;/strong&gt; — for example, no MX record or no SPF record.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What it means&lt;/strong&gt; — can the domain receive mail? Can another server spoof it?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What to do first&lt;/strong&gt; — prioritize authentication and routing before cosmetic improvements.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why only one tool?
&lt;/h2&gt;

&lt;p&gt;MCP servers can become a pile of endpoints. I kept this one focused because the main agent use case is simple: &lt;em&gt;check a domain before you send from it or recommend it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;One tool also makes the permission model easy to reason about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is read-only.&lt;/li&gt;
&lt;li&gt;It does not touch a mailbox.&lt;/li&gt;
&lt;li&gt;It does not send email.&lt;/li&gt;
&lt;li&gt;It does not require credentials.&lt;/li&gt;
&lt;li&gt;It returns DNS-based evidence, not a guarantee of inbox placement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DNSBL checks, mailbox access, and sending would each deserve a separate tool with a separate threat model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits and caching
&lt;/h2&gt;

&lt;p&gt;The MCP server shares the same scan budget as the REST API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;60 requests per hour per IP&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;No API key or account&lt;/li&gt;
&lt;li&gt;MCP calls run a fresh scan&lt;/li&gt;
&lt;li&gt;The REST endpoint caches same-domain results for 24 hours&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That difference is intentional. The REST API is optimized for repeated checks and dashboards; the MCP path is optimized for an agent asking once during a task.&lt;/p&gt;

&lt;p&gt;If your agent needs the full blacklist report, send it to the web scanner or use the REST API's documented surface. The MCP tool says &lt;code&gt;no blacklist&lt;/code&gt; in its description so the agent does not silently assume otherwise.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the score means
&lt;/h2&gt;

&lt;p&gt;The public API returns &lt;code&gt;maxScore: 80&lt;/code&gt;. That is not a bug.&lt;/p&gt;

&lt;p&gt;The score is normalized against the checks that actually ran. Because DNSBL checks are excluded from the public API, the maximum available score is 80 rather than 100. A "perfect" cheap scan should not be presented as a perfect full deliverability audit.&lt;/p&gt;

&lt;p&gt;This is the kind of detail that matters when an agent is summarizing results for a human. The agent should say what was checked, not just repeat a number.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small example response
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;69&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxScore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"grade"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"mx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"spf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dkim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dmarc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rdns"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fixes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"rdns"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mx"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The real payload includes the record values and reasons behind each status. That is what makes the result useful to an agent: it can explain the finding instead of only returning a grade.&lt;/p&gt;

&lt;h2&gt;
  
  
  When this is useful
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An agent onboards a new client domain and checks it before sending.&lt;/li&gt;
&lt;li&gt;A developer asks an IDE agent to sanity-check a domain from a README.&lt;/li&gt;
&lt;li&gt;A support assistant flags a customer domain with no SPF or DMARC.&lt;/li&gt;
&lt;li&gt;A sales agent checks a prospect's domain before recommending an email setup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When it is not enough
&lt;/h2&gt;

&lt;p&gt;This is not a replacement for a full deliverability audit. It does not inspect mailbox placement, content, sending reputation, or live blacklist listings. It is a fast, cheap first pass.&lt;/p&gt;

&lt;p&gt;If you need the complete view, use the web scanner:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full scan: &lt;a href="https://mailers.email/scan/" rel="noopener noreferrer"&gt;mailers.email/scan&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;API and MCP docs: &lt;a href="https://mailers.email/api/scan/" rel="noopener noreferrer"&gt;mailers.email/api/scan&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub README and examples: &lt;a href="https://github.com/xusteve/mailers-scan-api" rel="noopener noreferrer"&gt;github.com/xusteve/mailers-scan-api&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you connect it to an agent, I would like to hear what the agent does with the results — especially where it overstates the score or misses a fix-first priority.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>email</category>
      <category>api</category>
    </item>
    <item>
      <title>Building an anonymous email-domain scan API on Cloudflare Workers</title>
      <dc:creator>xusteve</dc:creator>
      <pubDate>Mon, 02 Nov 2026 06:47:01 +0000</pubDate>
      <link>https://dev.to/xusteve/building-an-anonymous-email-domain-scan-api-on-cloudflare-workers-h4k</link>
      <guid>https://dev.to/xusteve/building-an-anonymous-email-domain-scan-api-on-cloudflare-workers-h4k</guid>
      <description>&lt;p&gt;Mailers.email exposes domain health checks as both a REST API and an MCP server. This post covers the API contract, the Cloudflare Worker request path, caching, rate limiting and the design decisions behind no-key access.&lt;/p&gt;

&lt;p&gt;The short version: &lt;code&gt;POST /v1/scan&lt;/code&gt; accepts a domain, runs a small set of DNS checks through Cloudflare DoH, returns a structured score plus fix-first findings, and requires no account or API key. The public API is intentionally cheaper than the full web scan: blacklist checks are excluded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why email-domain checking works well as an API
&lt;/h2&gt;

&lt;p&gt;Email configuration is mostly public DNS. MX, SPF, DKIM, DMARC, reverse DNS and domain age can all be inspected without mailbox access.&lt;/p&gt;

&lt;p&gt;That makes the core check a good fit for a small, read-only API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one domain in&lt;/li&gt;
&lt;li&gt;a handful of DNS lookups out&lt;/li&gt;
&lt;li&gt;a deterministic result that another service or agent can explain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is also safe to automate. There is no message content, no mailbox login and no sending capability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contract of &lt;code&gt;POST /v1/scan&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.mailers.email/v1/scan &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"domain":"example.com"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful response contains the domain, score, grade, confidence, individual checks and a &lt;code&gt;fixes&lt;/code&gt; array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;69&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"maxScore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"grade"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"checks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"mx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"spf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dkim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dmarc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rdns"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fixes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"rdns"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mx"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"meta"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cached"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"quota"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"remaining"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hour"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The real response includes the record values and reasons for each check; the example is abbreviated.&lt;/p&gt;

&lt;h3&gt;
  
  
  About &lt;code&gt;maxScore&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The public API returns &lt;code&gt;maxScore: 80&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is deliberate. The score is normalized against the checks that actually ran. DNSBL checks are excluded from the public API, so a perfect public scan should not be presented as a perfect full deliverability audit.&lt;/p&gt;

&lt;p&gt;That distinction matters when another system summarizes the result. A score should come with the scope it was measured against.&lt;/p&gt;

&lt;h2&gt;
  
  
  Request flow: Cloudflare Worker + DoH
&lt;/h2&gt;

&lt;p&gt;The API is served by a Cloudflare Worker.&lt;/p&gt;

&lt;p&gt;At a high level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /v1/scan
  -&amp;gt; validate domain
  -&amp;gt; check IP rate limit
  -&amp;gt; check KV cache by domain
  -&amp;gt; query Cloudflare DoH for DNS records
  -&amp;gt; build checks and score
  -&amp;gt; cache result for 24 hours
  -&amp;gt; return JSON
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Worker uses Cloudflare's DNS-over-HTTPS resolver for live DNS. No separate DNS service sits in the middle.&lt;/p&gt;

&lt;p&gt;The scan currently covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MX records&lt;/li&gt;
&lt;li&gt;SPF record and lookup count&lt;/li&gt;
&lt;li&gt;DKIM at common selectors&lt;/li&gt;
&lt;li&gt;DMARC policy&lt;/li&gt;
&lt;li&gt;reverse DNS / PTR&lt;/li&gt;
&lt;li&gt;domain age via RDAP where available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The checks are deliberately bounded. This is not a full inbox-placement or reputation test.&lt;/p&gt;

&lt;h2&gt;
  
  
  24-hour cache for the same domain
&lt;/h2&gt;

&lt;p&gt;DNS records do not change every minute. Caching the result by domain keeps repeated checks cheap and avoids hammering upstream resolvers.&lt;/p&gt;

&lt;p&gt;The cache is stored in Cloudflare KV with a 24-hour TTL. Cached requests still count toward the rate limit, but they skip the DNS work. The response includes &lt;code&gt;meta.cached&lt;/code&gt; so callers know whether they received a cached result.&lt;/p&gt;

&lt;p&gt;The cache is per domain, not per user. There is no account or API key to associate with a query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate limiting: atomic counters with Durable Objects
&lt;/h2&gt;

&lt;p&gt;Anonymous APIs need a limit that works under concurrency.&lt;/p&gt;

&lt;p&gt;An ordinary read-modify-write sequence in KV is not atomic: two simultaneous requests can read the same counter and both write the same increment. For a 60/hour limit, that is a quiet way to let bursts through.&lt;/p&gt;

&lt;p&gt;The scan API uses a Durable Object as a per-IP serialization point for counter updates. The object handles the counter operation, so increments are atomic. The API uses the same mechanism for the shared scan budget.&lt;/p&gt;

&lt;p&gt;The public scan limit is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;60 requests/hour/IP&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;shared by the REST scan, MCP &lt;code&gt;scan_domain&lt;/code&gt; and the interactive scan&lt;/li&gt;
&lt;li&gt;no API key or account&lt;/li&gt;
&lt;li&gt;HTTP 429 when exceeded&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The separate free DNS tool pages have their own stricter limit of 30/minute and 300/hour. The scan API and the tool pages are related products, but they are not the same quota.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the API excludes DNSBL
&lt;/h2&gt;

&lt;p&gt;Blacklist checks are 32+ additional DNS queries per domain. That is a different cost profile from the six cheap checks above.&lt;/p&gt;

&lt;p&gt;Keeping DNSBL out of the public API has three benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A scan stays fast and inexpensive.&lt;/li&gt;
&lt;li&gt;A single &lt;code&gt;scan_domain&lt;/code&gt; MCP call does not fan out into dozens of queries.&lt;/li&gt;
&lt;li&gt;The API's scope stays honest: it reports DNS configuration, not live reputation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The full blacklist scan lives in the web app at &lt;a href="https://mailers.email/scan/" rel="noopener noreferrer"&gt;mailers.email/scan&lt;/a&gt;. The API documentation also states that the public scan does not include blacklist checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP: &lt;code&gt;scan_domain&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The same cheap scan is available as a remote MCP server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mailers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.mailers.email/mcp"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It exposes exactly one tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scan_domain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tool is read-only. It does not access a mailbox, send email or require credentials. This keeps the permission model narrow enough for an agent to use without a long explanation.&lt;/p&gt;

&lt;p&gt;One implementation detail: MCP calls run a fresh scan rather than using the REST API's 24-hour cache. The REST endpoint is optimized for repeated checks; the MCP path is optimized for an agent asking once during a task.&lt;/p&gt;

&lt;p&gt;I wrote a separate MCP-focused post with client configuration and agent examples:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/xusteve/giving-ai-agents-an-email-deliverability-check-with-mcp-5en7"&gt;Giving AI agents an email deliverability check with MCP&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Error handling and API behavior
&lt;/h2&gt;

&lt;p&gt;The API returns JSON for normal application errors rather than an HTML error page.&lt;/p&gt;

&lt;p&gt;Expected behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;400&lt;/code&gt; for an invalid or missing domain&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;405&lt;/code&gt; for a non-POST request&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;410&lt;/code&gt; for retired API routes; only &lt;code&gt;POST /v1/scan&lt;/code&gt; remains public&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;429&lt;/code&gt; when the IP budget is exhausted&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;502&lt;/code&gt; when an upstream scan cannot complete&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The health endpoint is available at &lt;code&gt;https://api.mailers.email/health&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy and limits, stated plainly
&lt;/h2&gt;

&lt;p&gt;This is a hosted service, not a self-hosted open-source scanner.&lt;/p&gt;

&lt;p&gt;What is true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no account and no API key&lt;/li&gt;
&lt;li&gt;DNS-based checks only&lt;/li&gt;
&lt;li&gt;per-IP rate limits&lt;/li&gt;
&lt;li&gt;same-domain results cached for 24 hours&lt;/li&gt;
&lt;li&gt;no mailbox access and no email sending&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What is not claimed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no inbox-placement guarantee&lt;/li&gt;
&lt;li&gt;no permanent "no data stored" claim&lt;/li&gt;
&lt;li&gt;no blacklist check in the public API&lt;/li&gt;
&lt;li&gt;no 100-point full audit score&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Precise limits are more useful than vague promises.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would improve next
&lt;/h2&gt;

&lt;p&gt;The current design is deliberately small. The next useful steps would be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Publish an OpenAPI file and client examples.&lt;/li&gt;
&lt;li&gt;Add a small response-schema version field.&lt;/li&gt;
&lt;li&gt;Add more granular error details for DNS timeouts.&lt;/li&gt;
&lt;li&gt;Keep the public API cheap while leaving heavier reputation checks in the web scanner.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;API and MCP docs: &lt;a href="https://mailers.email/api/scan/" rel="noopener noreferrer"&gt;mailers.email/api/scan&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Full web scanner: &lt;a href="https://mailers.email/scan/" rel="noopener noreferrer"&gt;mailers.email/scan&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub README and examples: &lt;a href="https://github.com/xusteve/mailers-scan-api" rel="noopener noreferrer"&gt;github.com/xusteve/mailers-scan-api&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Status: &lt;a href="https://mailers.email/status/" rel="noopener noreferrer"&gt;mailers.email/status&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build something with the API or the MCP server, I would like to hear what worked and what was missing.&lt;/p&gt;

</description>
      <category>cloudflare</category>
      <category>email</category>
      <category>api</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Zero-Cost Uptime Monitoring + Status Page on Cloudflare's Free Tier</title>
      <dc:creator>xusteve</dc:creator>
      <pubDate>Sun, 16 Aug 2026 14:51:16 +0000</pubDate>
      <link>https://dev.to/xusteve/zero-cost-uptime-monitoring-status-page-on-cloudflares-free-tier-cak</link>
      <guid>https://dev.to/xusteve/zero-cost-uptime-monitoring-status-page-on-cloudflares-free-tier-cak</guid>
      <description>&lt;h1&gt;
  
  
  Zero-Cost Uptime Monitoring + Status Page on Cloudflare's Free Tier
&lt;/h1&gt;

&lt;p&gt;Monitoring your websites shouldn't cost money or require a VPS. Most uptime tools need a server, a database, or a paid plan — even small hobby projects end up with a $5/mo bill just to check if a site is up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MonitorFlare&lt;/strong&gt; is a self-hosted uptime monitoring platform with a beautiful public status page that runs &lt;strong&gt;entirely on Cloudflare's free tier&lt;/strong&gt;: Workers + D1 + Pages. No server, no VPS, no monthly costs — and it deploys in about 60 seconds with a one-click button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://deploy.workers.cloudflare.com/?url=https://github.com/xusteve/MonitorFlare" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeploy.workers.cloudflare.com%2Fbutton" alt="Deploy to Cloudflare" width="184" height="39"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's an enhanced fork of &lt;a href="https://github.com/nianshu2022/Uptime-Monitor" rel="noopener noreferrer"&gt;Uptime-Monitor&lt;/a&gt; (MIT), with a Vue 3 frontend, PWA support, and i18n in 9 languages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why MonitorFlare
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;MonitorFlare&lt;/th&gt;
&lt;th&gt;Typical hosted monitor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$0/month&lt;/strong&gt; (Cloudflare free tier)&lt;/td&gt;
&lt;td&gt;$5–50/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploy&lt;/td&gt;
&lt;td&gt;One click, no CLI&lt;/td&gt;
&lt;td&gt;Signup + config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status page&lt;/td&gt;
&lt;td&gt;Built-in, PWA, RSS/email subscribe&lt;/td&gt;
&lt;td&gt;Often extra&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notifications&lt;/td&gt;
&lt;td&gt;9 channels&lt;/td&gt;
&lt;td&gt;1–3 channels&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What it monitors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTTP/HTTPS&lt;/strong&gt; — GET/POST/PUT/HEAD, custom headers, body, keyword validation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS&lt;/strong&gt; — A/AAAA/CNAME/MX/TXT/NS via DoH&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TCP ports&lt;/strong&gt; — via the Workers TCP Socket API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL certificate &amp;amp; domain expiry&lt;/strong&gt; — via crt.sh + rdap.org&lt;/li&gt;
&lt;li&gt;Error-rate thresholds and consecutive-failure escalation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Notifications: 9 channels
&lt;/h2&gt;

&lt;p&gt;DingTalk · WeCom (WeChat Work) · Feishu (Lark) · Telegram · Slack · Discord · ntfy · generic Webhook — plus &lt;strong&gt;email with 5 providers&lt;/strong&gt; (Resend, SendGrid, Mailgun, Postmark, AWS SES).&lt;/p&gt;

&lt;h2&gt;
  
  
  Status page
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tag groups, incident timeline, scheduled maintenance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email subscription&lt;/strong&gt; for incident updates + &lt;strong&gt;RSS/Atom feed&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Custom branding, dark/light themes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PWA&lt;/strong&gt; — installable, offline-ready&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Internationalization
&lt;/h2&gt;

&lt;p&gt;9 languages: English · 简体中文 · 繁體中文 · 日本語 · 한국어 · Deutsch · Français · Italiano · Español. Configurable timezone for all timestamps and alerts.&lt;/p&gt;

&lt;h2&gt;
  
  
  One-click deploy
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Click the Deploy button above&lt;/li&gt;
&lt;li&gt;Sign in to Cloudflare&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;ADMIN_API_KEY&lt;/code&gt; (your admin password)&lt;/li&gt;
&lt;li&gt;Done — the D1 schema auto-creates on first visit&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Or use GitHub Actions CI/CD, or manual &lt;code&gt;wrangler&lt;/code&gt; deploy — all documented in the README.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live demo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Status page: &lt;a href="https://monitorflare.csr.plus" rel="noopener noreferrer"&gt;https://monitorflare.csr.plus&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Public API: &lt;code&gt;GET /api/status&lt;/code&gt; returns JSON for integrations&lt;/li&gt;
&lt;li&gt;Feed: &lt;code&gt;GET /feed.xml&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Open source
&lt;/h2&gt;

&lt;p&gt;MIT licensed, fully self-hosted, no telemetry. Fork it, deploy it, own your monitoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/xusteve/MonitorFlare" rel="noopener noreferrer"&gt;⭐ Star MonitorFlare on GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I built MonitorFlare.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>monitoring</category>
      <category>uptime</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Generate a Certificate Signing Request with curl — No OpenSSL Needed</title>
      <dc:creator>xusteve</dc:creator>
      <pubDate>Sat, 15 Aug 2026 13:58:57 +0000</pubDate>
      <link>https://dev.to/xusteve/generate-a-certificate-signing-request-with-curl-no-openssl-needed-4ph9</link>
      <guid>https://dev.to/xusteve/generate-a-certificate-signing-request-with-curl-no-openssl-needed-4ph9</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqfbingu00u2q40ppccft.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqfbingu00u2q40ppccft.png" alt="CSR.plus" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Generate a Certificate Signing Request with curl — No OpenSSL Needed
&lt;/h1&gt;

&lt;p&gt;If you've ever ordered an SSL/TLS certificate, you know the drill: fire up a terminal, type &lt;code&gt;openssl req -new ...&lt;/code&gt;, and pray you get the flags right. One wrong &lt;code&gt;-subj&lt;/code&gt; format, one forgotten SAN, and your certificate authority sends the request back with a cryptic error.&lt;/p&gt;

&lt;p&gt;There's a simpler way: &lt;strong&gt;generate a Certificate Signing Request (CSR) with a single curl command&lt;/strong&gt; — no OpenSSL, no config files, no interactive prompts.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a CSR, really?
&lt;/h2&gt;

&lt;p&gt;A CSR is just a block of encoded text (PKCS#10) that contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your &lt;strong&gt;public key&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Your domain name(s) — the Common Name (CN) and Subject Alternative Names (SANs)&lt;/li&gt;
&lt;li&gt;Optional organization details (O, OU, C, ST, L) for OV/EV certificates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You send it to a Certificate Authority (CA) like Let's Encrypt, DigiCert, or Sectigo. They verify you control the domain, sign a certificate using your public key, and hand it back. Your &lt;strong&gt;private key never leaves your machine&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The old way: OpenSSL
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl req &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:2048 &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-keyout&lt;/span&gt; example.com.key &lt;span class="nt"&gt;-out&lt;/span&gt; example.com.csr &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/C=US/ST=California/O=ACME Inc/CN=example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Works, but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have to remember the &lt;code&gt;-subj&lt;/code&gt; syntax and every flag&lt;/li&gt;
&lt;li&gt;Interactive prompts if you forget &lt;code&gt;-subj&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Easy to mess up SANs without a config file&lt;/li&gt;
&lt;li&gt;Painful inside CI/CD scripts where you want everything automated&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The new way: one curl command
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://csr.plus/docs/api" rel="noopener noreferrer"&gt;csr.plus API&lt;/a&gt; generates a standards-compliant CSR and private key for you. No signup, no API key, no account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://csr.plus/api/generate &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"common_name": "example.com"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole thing. Here's what comes back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"csr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-----BEGIN CERTIFICATE REQUEST-----&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;MIIB..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-----BEGIN PRIVATE KEY-----&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;MIIE..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"algorithm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RSA-2048"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-15T10:00:00Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the &lt;code&gt;csr&lt;/code&gt; into your CA's enrollment form, keep the &lt;code&gt;private_key&lt;/code&gt; safe, done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  With SANs (multi-domain certificates)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://csr.plus/api/generate &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "common_name": "example.com",
    "sans": ["www.example.com", "api.example.com"]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ECDSA keys
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://csr.plus/api/generate &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "common_name": "example.com",
    "key_type": "ecdsa",
    "key_size": "P-256"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  In a CI/CD pipeline (Shell + jq)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://csr.plus/api/generate &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"common_name": "example.com", "sans": ["www.example.com"]}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .csr &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; example.com.csr

curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://csr.plus/api/generate &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"common_name": "example.com", "sans": ["www.example.com"]}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .private_key &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; example.com.key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  In Python
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://csr.plus/api/generate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;common_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sans&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;www.example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;example.com.csr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;csr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;example.com.key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;private_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify the result
&lt;/h3&gt;

&lt;p&gt;Never trust blindly — verify the CSR locally before submitting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl req &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-text&lt;/span&gt; &lt;span class="nt"&gt;-verify&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; example.com.csr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;verify OK&lt;/code&gt; and your subject fields. (OpenSSL is still useful for &lt;em&gt;verifying&lt;/em&gt; — you just don't need it to &lt;em&gt;generate&lt;/em&gt; anymore.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it private? Here's the security model
&lt;/h2&gt;

&lt;p&gt;The obvious question: "my private key is generated on &lt;em&gt;their&lt;/em&gt; server?"&lt;/p&gt;

&lt;p&gt;Here's how &lt;a href="https://csr.plus" rel="noopener noreferrer"&gt;csr.plus&lt;/a&gt; handles it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero storage&lt;/strong&gt; — key pairs are generated in server memory and destroyed immediately after the response&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero logging&lt;/strong&gt; — generation never writes logs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTPS only&lt;/strong&gt; with &lt;code&gt;Cache-Control: no-store&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optional passphrase&lt;/strong&gt; — encrypt the private key (PKCS#8) before it's returned to you&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limited&lt;/strong&gt; — 10 requests/minute/IP to keep the service free&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Honest caveat:&lt;/strong&gt; like any online CSR tool, this is designed for development, testing, and automation workflows. For production certificates, generate the key offline on your own hardware. The API docs say the same thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I built this
&lt;/h2&gt;

&lt;p&gt;The repo is open source — &lt;a href="https://github.com/xusteve/csr-plus-generator" rel="noopener noreferrer"&gt;csr-plus-generator&lt;/a&gt;. The goal is simple: make CSR generation boring. One command, no signup, no flags to memorize, works from any CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;Check out the other free tools too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://csr.plus" rel="noopener noreferrer"&gt;CSR Generator&lt;/a&gt; — generate CSRs right in your browser&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://csr.plus/check" rel="noopener noreferrer"&gt;SSL Certificate Checker&lt;/a&gt; — inspect any domain's cert: issuer, expiry, chain&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://csr.plus/decoder" rel="noopener noreferrer"&gt;CSR &amp;amp; Certificate Decoder&lt;/a&gt; — decode and inspect any CSR&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://csr.plus/monitor" rel="noopener noreferrer"&gt;SSL Expiry Monitor&lt;/a&gt; — 24/7 expiry monitoring with email alerts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy certifying 🎉&lt;/p&gt;




&lt;p&gt;We are live on Product Hunt today! If CSR.plus has been useful for you, an upvote would mean a lot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.producthunt.com/posts/csr-plus" rel="noopener noreferrer"&gt;&lt;strong&gt;Upvote CSR.plus on Product Hunt&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openssl</category>
      <category>devtools</category>
      <category>ssl</category>
      <category>api</category>
    </item>
    <item>
      <title>The Ultimate WordPress Security Checklist for 2026</title>
      <dc:creator>xusteve</dc:creator>
      <pubDate>Sun, 21 Jun 2026 15:45:41 +0000</pubDate>
      <link>https://dev.to/xusteve/the-ultimate-wordpress-security-checklist-for-2026-255m</link>
      <guid>https://dev.to/xusteve/the-ultimate-wordpress-security-checklist-for-2026-255m</guid>
      <description>&lt;h1&gt;
  
  
  The Ultimate WordPress Security Checklist for 2026
&lt;/h1&gt;

&lt;p&gt;WordPress powers over 43% of all websites — making it the most popular CMS in the world and, by extension, the most targeted platform for attackers. In 2024 alone, over 24,000 WordPress plugins were flagged for security vulnerabilities. If you're running a WordPress site, you need a systematic approach to security, not a reactive one.&lt;/p&gt;

&lt;p&gt;This checklist covers everything you need to lock down your WordPress installation — from basic hardening to advanced monitoring. At the end, we'll show you how to automate most of these checks.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Core WordPress Hardening
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Keep Everything Updated
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WordPress core&lt;/strong&gt;: Enable auto-updates for minor releases; test major releases on staging first&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Themes &amp;amp; plugins&lt;/strong&gt;: Delete unused ones. Outdated plugins are the #1 attack vector — 56% of known vulnerabilities come from third-party plugins&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHP version&lt;/strong&gt;: Run PHP 8.1+ (7.4 reached EOL in November 2022 and has known security holes)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Strengthen Authentication
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enforce strong passwords&lt;/strong&gt; for all users (use a password manager)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable Two-Factor Authentication (2FA)&lt;/strong&gt; — the single most effective step against brute force&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit login attempts&lt;/strong&gt; — plugins like Wordfence or CSF (ConfigServer Security &amp;amp; Firewall) can block IPs after 3-5 failed attempts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change the default "admin" username&lt;/strong&gt; — if you're still using it, create a new admin and delete the old one&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Secure wp-config.php
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Move wp-config.php one directory above the web root if possible&lt;/span&gt;
&lt;span class="c1"&gt;// Add these lines:&lt;/span&gt;
&lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DISALLOW_FILE_EDIT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;       &lt;span class="c1"&gt;// Disable file editor in admin&lt;/span&gt;
&lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'AUTOSAVE_INTERVAL'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;          &lt;span class="c1"&gt;// Reduce autosave frequency&lt;/span&gt;
&lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WP_POST_REVISIONS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;            &lt;span class="c1"&gt;// Limit post revisions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. File System &amp;amp; Server Security
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Protect Sensitive Files
&lt;/h3&gt;

&lt;p&gt;Block access to these files via &lt;code&gt;.htaccess&lt;/code&gt; or nginx config:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;wp-config.php&lt;/code&gt; — contains DB credentials&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.git/&lt;/code&gt; and &lt;code&gt;.env&lt;/code&gt; — if using Git deployment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;readme.html&lt;/code&gt; and &lt;code&gt;license.txt&lt;/code&gt; in the root — these leak your WordPress version&lt;/li&gt;
&lt;li&gt;XML-RPC if not needed (often used for DDoS amplification)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Directory Permissions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;wp-config.php&lt;/code&gt;: 400 or 440 (read-only)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wp-content/&lt;/code&gt;: 755 (directories), 644 (files)&lt;/li&gt;
&lt;li&gt;Never use 777 — it's an open invitation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disable Directory Browsing
&lt;/h3&gt;

&lt;p&gt;Add to &lt;code&gt;.htaccess&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="nc"&gt;Options&lt;/span&gt; -Indexes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Implement a Web Application Firewall (WAF)
&lt;/h3&gt;

&lt;p&gt;A WAF sits in front of your site and blocks malicious requests before they reach WordPress. Options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare&lt;/strong&gt; (free tier available) — blocks SQL injection, XSS, and known bad bots&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wordfence&lt;/strong&gt; — WordPress-native WAF with real-time threat defense feed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sucuri&lt;/strong&gt; — CDN + WAF with DDoS protection&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Database Security
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Change the Table Prefix
&lt;/h3&gt;

&lt;p&gt;The default &lt;code&gt;wp_&lt;/code&gt; prefix is well-known. Change it during installation, or use a plugin like WP-DBManager to rename it on an existing site.&lt;/p&gt;

&lt;h3&gt;
  
  
  Regular Backups (3-2-1 Rule)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3 copies&lt;/strong&gt; of your data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2 different media&lt;/strong&gt; (server + cloud)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1 offsite&lt;/strong&gt; (Google Drive, Dropbox, S3)&lt;/li&gt;
&lt;li&gt;Recommended: UpdraftPlus, BlogVault, or your host's built-in backup&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Limit Database Access
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a dedicated MySQL user with only the necessary privileges&lt;/li&gt;
&lt;li&gt;Never use the root MySQL user for WordPress&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. SSL/TLS &amp;amp; HTTPS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install an SSL Certificate
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Free options: Let's Encrypt (most hosts support one-click install)&lt;/li&gt;
&lt;li&gt;Verify HTTPS is enforced site-wide (301 redirect HTTP → HTTPS)&lt;/li&gt;
&lt;li&gt;Check for mixed content warnings (Chrome DevTools → Console)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  HSTS Header
&lt;/h3&gt;

&lt;p&gt;Add to your server config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Strict-Transport-Security&lt;/span&gt; &lt;span class="s"&gt;"max-age=31536000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;includeSubDomains&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;preload"&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Content Security Policy (CSP)
&lt;/h2&gt;

&lt;p&gt;A CSP header tells browsers which resources are allowed to load. Start with a report-only mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then monitor the reports and tighten over time.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Monitoring &amp;amp; Alerts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Uptime Monitoring
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UptimeRobot&lt;/strong&gt; (free, 50 monitors)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;StatusCake&lt;/strong&gt; — also monitors SSL expiry&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Change Detection
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WPScan&lt;/strong&gt; — checks if your WordPress version/plugins have known vulnerabilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WordPress Site Health&lt;/strong&gt; (built-in, Tools → Site Health)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Security Logging
&lt;/h3&gt;

&lt;p&gt;Enable WordPress debug logging (&lt;code&gt;WP_DEBUG_LOG&lt;/code&gt; in wp-config.php) and review regularly. For production, use a plugin like WP Activity Log.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Common Mistakes That Undermine Everything
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mistake&lt;/th&gt;
&lt;th&gt;Why It's Dangerous&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Using &lt;code&gt;admin&lt;/code&gt; as username&lt;/td&gt;
&lt;td&gt;Brute force bots target it first&lt;/td&gt;
&lt;td&gt;Create new admin, delete &lt;code&gt;admin&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No 2FA&lt;/td&gt;
&lt;td&gt;Credential stuffing is rampant&lt;/td&gt;
&lt;td&gt;Use Wordfence 2FA or WP 2FA plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outdated PHP&lt;/td&gt;
&lt;td&gt;PHP 7.x has unpatched CVEs&lt;/td&gt;
&lt;td&gt;Upgrade to PHP 8.1+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nulled plugins/themes&lt;/td&gt;
&lt;td&gt;Often contain backdoors&lt;/td&gt;
&lt;td&gt;Buy from official sources only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No backups&lt;/td&gt;
&lt;td&gt;Ransomware can wipe everything&lt;/td&gt;
&lt;td&gt;Automated offsite backups, test restores&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XML-RPC enabled&lt;/td&gt;
&lt;td&gt;Used for DDoS and brute force&lt;/td&gt;
&lt;td&gt;Disable if not using Jetpack/mobile apps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  8. Automate Your Security Audit
&lt;/h2&gt;

&lt;p&gt;Manually checking all 37+ security items above every month is unrealistic. That's why we built &lt;strong&gt;&lt;a href="https://app.wpseo.help" rel="noopener noreferrer"&gt;wpSEO&lt;/a&gt;&lt;/strong&gt; — a free tool that automates WordPress security scanning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;37 security checks&lt;/strong&gt; — version exposure, security headers, WAF, 2FA, PHP version, and more&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;125+ total checks&lt;/strong&gt; combining security + SEO in one report&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;No signup required&lt;/strong&gt; — paste your URL and get results in seconds&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;10 languages&lt;/strong&gt; — available in English, Chinese, Japanese, Korean, German, Russian, Arabic, French, Spanish, Portuguese&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;PDF reports&lt;/strong&gt; — export and share with your team or clients&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Actionable fixes&lt;/strong&gt; — every check includes a "how to fix" description&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Try it free&lt;/strong&gt;: &lt;a href="https://app.wpseo.help" rel="noopener noreferrer"&gt;https://app.wpseo.help&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Word
&lt;/h2&gt;

&lt;p&gt;WordPress security isn't a one-time setup — it's ongoing hygiene. The checklist above covers the fundamentals. Run through it quarterly, stay on top of updates, and use an automated scanner to catch what you miss.&lt;/p&gt;

&lt;p&gt;Your WordPress site is your business. Lock it down.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Got questions about WordPress security? Drop a comment or reach out — happy to help.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>security</category>
      <category>seo</category>
      <category>webdev</category>
    </item>
    <item>
      <title>digkeywords.net - SEO Keyword Tool</title>
      <dc:creator>xusteve</dc:creator>
      <pubDate>Thu, 15 Oct 2020 11:03:46 +0000</pubDate>
      <link>https://dev.to/xusteve/digkeywords-net-seo-keyword-tool-101k</link>
      <guid>https://dev.to/xusteve/digkeywords-net-seo-keyword-tool-101k</guid>
      <description>&lt;p&gt;All in One SEO KeyWord Research Tool&lt;br&gt;
&lt;a href="https://digkeywords.net" rel="noopener noreferrer"&gt;https://digkeywords.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Google keyword tool&lt;br&gt;
Bing keyword tool&lt;br&gt;
Amazon keyword tool&lt;br&gt;
eBay keyword tool&lt;br&gt;
YouTube keyword tool&lt;br&gt;
Yahoo keyword tool&lt;br&gt;
Wikipedia keyword tool&lt;/p&gt;

&lt;p&gt;FREE All In One SEO Keyword Research Tool that helps you to get the top keyword ideas or long tail keywords from most popular websites such as Google, Bing, Yahoo, Amazon, Ebay, Youtube, Wikipedia and checks the domain names already registered or still available of those keywords to find the BEST Keyword Domain Names for you.&lt;/p&gt;

&lt;p&gt;You also can use our Free Google Keyword Rank Checker to Check Your Keyword Positions in Google Search Results for your sites.&lt;br&gt;
Why Do You Need Dig Keywords Tool?&lt;/p&gt;

&lt;p&gt;Dig KeyWords Tool is the BEST alternative keyword research tool to Google keyword planner and other keyword research tools&lt;br&gt;
Dig KeyWords Tool helps to find long-tail keywords that are ignored by you or your competitors!&lt;br&gt;
Dig KeyWords Tool helps you find the right keywords for SEO and content creation.&lt;br&gt;
Dig KeyWords Tool helps you dig the relevant keywords that you target with your CPC ads!&lt;/p&gt;

</description>
      <category>keyword</category>
    </item>
    <item>
      <title>Whoisdomainthis - Domain Statistic Search Tool</title>
      <dc:creator>xusteve</dc:creator>
      <pubDate>Sat, 03 Oct 2020 23:59:29 +0000</pubDate>
      <link>https://dev.to/xusteve/whoisdomainthis-domain-statistic-search-tool-22f1</link>
      <guid>https://dev.to/xusteve/whoisdomainthis-domain-statistic-search-tool-22f1</guid>
      <description>&lt;p&gt;Free Domain Statistic Search Tool shows you related information about domain name such as WHOIS record/DNS records, number of pages indexed in Search Engines (Google, Yahoo, Bing), Server IP and Geo location, Alexa statistic, Like count and more...&lt;/p&gt;

&lt;p&gt;WHOIS Lookup Tool&lt;br&gt;
&lt;a href="https://whoisdomainthis.com/whois.html" rel="noopener noreferrer"&gt;https://whoisdomainthis.com/whois.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DNS Record Lookup&lt;br&gt;
&lt;a href="https://whoisdomainthis.com/dns.html" rel="noopener noreferrer"&gt;https://whoisdomainthis.com/dns.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;IP Address Lookup&lt;br&gt;
&lt;a href="https://whoisdomainthis.com/ip.html" rel="noopener noreferrer"&gt;https://whoisdomainthis.com/ip.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What Is My Public IP Address?&lt;br&gt;
&lt;a href="https://whoisdomainthis.com/whatismyip.html" rel="noopener noreferrer"&gt;https://whoisdomainthis.com/whatismyip.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Favicon Generator&lt;br&gt;
&lt;a href="https://whoisdomainthis.com/favicon/" rel="noopener noreferrer"&gt;https://whoisdomainthis.com/favicon/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>DNS.glass checks the health and configuration &amp; provides DNS report and mail servers report.</title>
      <dc:creator>xusteve</dc:creator>
      <pubDate>Sat, 03 Oct 2020 23:54:18 +0000</pubDate>
      <link>https://dev.to/xusteve/dns-glass-checks-the-health-and-configuration-provides-dns-report-and-mail-servers-report-4ck4</link>
      <guid>https://dev.to/xusteve/dns-glass-checks-the-health-and-configuration-provides-dns-report-and-mail-servers-report-4ck4</guid>
      <description>&lt;p&gt;DNS.glass checks the health and configuration &amp;amp; provides DNS report and mail servers report.&lt;/p&gt;

&lt;p&gt;And provides suggestions to fix and improve your domain DNS, with references to protocols’ official documentation. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://dns.glass/google.com" rel="noopener noreferrer"&gt;https://dns.glass/google.com&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dns.glass/name.com" rel="noopener noreferrer"&gt;https://dns.glass/name.com&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dns.glass/bluehost.com" rel="noopener noreferrer"&gt;https://dns.glass/bluehost.com&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dns.glass/dns.glass" rel="noopener noreferrer"&gt;https://dns.glass/dns.glass&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>TempMail - Forget about spam, advertising mailings, hacking and attacking robots.</title>
      <dc:creator>xusteve</dc:creator>
      <pubDate>Sat, 03 Oct 2020 23:47:05 +0000</pubDate>
      <link>https://dev.to/xusteve/tempmail-forget-about-spam-advertising-mailings-hacking-and-attacking-robots-2bfg</link>
      <guid>https://dev.to/xusteve/tempmail-forget-about-spam-advertising-mailings-hacking-and-attacking-robots-2bfg</guid>
      <description>&lt;p&gt;&lt;a href="https://TempMail.at" rel="noopener noreferrer"&gt;https://TempMail.at&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free Temporary Email Address- tempmail, 10minutemail, throwaway email, fake-mail or trash-mail!&lt;/p&gt;

&lt;p&gt;Forget about spam, advertising mailings, hacking and attacking robots. Keep your real mailbox clean and secure. tempmail.at provides free temporary, secure, anonymous, free, disposable email address.&lt;/p&gt;

</description>
      <category>tempmail</category>
    </item>
  </channel>
</rss>
