Back to blog
guides 3 min read

Domain Locking and License Management for PHP Applications

Selling self-hosted PHP software means you need a licensing system that's both developer-friendly and tamper-resistant. A good license system should answer three questions: Who can run your software, where can they run it, and for how long?

The Anatomy of a PHP License

A modern PHP license typically contains:

{
    "key": "CL-XXXX-XXXX-XXXX-XXXX",
    "status": "active",
    "domain": "app.customer.com",
    "expires_at": "2027-01-01T00:00:00Z",
    "max_activations": 2,
    "activation_count": 1,
    "hmac": "a3f8..."
}

Each field serves a specific enforcement purpose.

Domain Locking

Domain locking restricts your software to run only on a specific hostname. During license validation, the runtime compares the current server's hostname against the licensed domain:

  • The check uses $_SERVER['HTTP_HOST'] or $_SERVER['SERVER_NAME']
  • Port numbers are stripped for comparison
  • The check is case-insensitive
  • localhost and 127.0.0.1 are typically whitelisted for development

This prevents a customer from buying one license and deploying on multiple production domains. If they need staging + production, they purchase additional activations or a separate license.

Activation Limits

Activation limits control how many server instances can run your software under a single license key:

  1. Customer enters the license key on a new server
  2. The runtime sends an activation request to your license API
  3. The API checks: has this key exceeded its max_activations?
  4. If not, it increments the counter and returns the encryption key
  5. If exceeded, activation is denied

This is particularly useful for per-seat or per-server pricing models. A "Starter" plan might allow 1 activation, while "Business" allows 5.

Expiry and Subscription Enforcement

For subscription-based products, license expiry is critical. The runtime checks expiry in two layers:

  • Local check — Fast, happens on every request. Compares expires_at against the server's current time.
  • Remote check (phone-home) — Happens periodically (e.g., every 24 hours). Verifies the license status against your central server, catching cases where a license was revoked or not renewed.

The phone-home interval is a balance between security and reliability. Too frequent, and a temporary network outage could disrupt your customer's service. Too infrequent, and revoked licenses continue working too long. 24 hours is a reasonable default for most products.

Remote Revocation

Sometimes you need to immediately disable a license — a customer's payment failed, they violated your terms, or they requested a refund. Remote revocation works because:

  1. You mark the license as "revoked" in your dashboard
  2. On the next phone-home check, the customer's server receives the revoked status
  3. The runtime invalidates the local license file
  4. The application stops executing and shows an activation screen

When protection is enforced at the bytecode level, the customer can't simply comment out the license check. The encrypted code won't run without a valid decryption key, and that key is only provided for active licenses.

HMAC Integrity

License files stored on the customer's server are signed with an HMAC (Hash-based Message Authentication Code). This prevents tampering — if someone edits the license file to change the expiry date or domain, the HMAC check fails and the license is rejected.

Implementation Tips

  • Grace period — Consider a 3-7 day grace period after expiry. This prevents customer disruption due to payment processing delays.
  • Offline tolerance — If the phone-home fails, don't immediately kill the application. Allow operation based on the last successful validation for a configurable period.
  • Clear error messages — When a license fails, tell the customer exactly why (expired, wrong domain, revoked) and how to fix it.
  • Re-activation flow — Make it easy for customers to enter a new license key without contacting support.
licensing domain-lock activation php

Related articles