How to Protect Your Minecraft Plugin from Piracy (Spigot & Paper)
A comprehensive guide to implementing plugin licensing, server binding, and API verification to protect your work from unauthorized distribution and piracy.
Why Plugin Piracy Is a Real Problem
If you've spent weeks building a Minecraft plugin—whether it's a custom economy system, world selector, or mini-game framework—you've probably thought about how to get paid for your work. But there's a problem: the moment your plugin goes public, it can be copied, redistributed, and repackaged by anyone, anywhere.
This isn't theoretical. Plugin piracy is widespread across the Minecraft community. Leaked JARs show up on file-sharing sites, discord servers, and private forums within days. Server owners share your plugin in group chats. Someone modifies your code and resells it as their own. Your revenue model falls apart.
The impact is real:
- Lost sales from pirated copies
- No control over who uses your plugin or where it runs
- Difficulty tracking which servers have legitimate licenses
- Inability to enforce updates or revoke access
- Reputational damage when leaked versions have bugs or are abandoned
The harder truth: obfuscation, string encryption, and hardcoded checks don't work. They make reversing harder, but not impossible. Determined pirates will find a way. You need a licensing system that doesn't rely on security-through-obscurity.
Common Ways Developers Try to Protect Plugins
Most plugin developers eventually try something to protect their work. But many approaches have fundamental flaws.
1. Hardcoded Checks
Embed license keys or server UUIDs directly in your plugin code:
if (!uuid.equals(UUID.fromString("12345678-1234-...")) {
Bukkit.shutdown();
}The problem: A decompiler shows these hardcoded UUIDs in seconds. Pirates just add their server UUID, recompile, and distribute. No protection at all.
2. Custom VPS with Manual Verification
You set up your own server, plugins call it to verify licenses, and you manually manage keys via a custom dashboard.
The problem: This is a lot of work. You're running a database, handling API downtime, managing authentication, and dealing with infrastructure. One outage blocks all your users. Plus, you still need to handle ProGuard, obfuscation, and other security layers yourself.
3. Obfuscation Only
Use ProGuard or similar tools to obfuscate your code, making it harder to reverse.
The problem: Obfuscation is a speed bump, not a wall. Advanced crackers bypass it routinely. And if you're obfuscating, you still need a protection mechanism. Obfuscation + nothing else = obfuscation + nothing else.
4. Per-Server API Keys (Without Binding)
You give each server owner a unique API key, they paste it into the config, and verification works.
The problem: If the API key is in the config file, pirates just copy the config. The key is now shared across many servers. You have no way to lock the key to a specific server or prevent its reuse. No true protection.
What all these approaches have in common: they rely on local checks. You're trying to solve a problem (piracy prevention) with tools that live on the user's machine. That's unwinnable. The moment someone has your JAR, they control the environment. You need a remote solution.
How a Plugin License API Works
A plugin license API flips the problem. Instead of trusting local checks, you verify licenses with a remote server that you control.
The Basic Flow
- Register your plugin: You create a plugin entry on the licensing platform and get a plugin slug (e.g.,
super-items). - Generate license keys: You issue unique license keys to your customers. Each key is tied to the plugin.
- Server owner configures the key: When someone buys your plugin, they add their license key to the plugin config and restart the server.
- Plugin verifies at startup: Your plugin calls the licensing API with the license key and server IP.
- API validates and responds: The API checks if the key is valid, hasn't expired, and is allowed on that server IP. It returns a simple yes/no.
- Plugin continues or halts: If valid, the plugin loads. If not, it disables itself.
- Heartbeat re-verification (optional): Periodically, the plugin pings the API to re-verify the license, catching revoked keys in real time.
Why This Works
The key point: The truth of whether a license is valid lives on your server, not the user's machine. Pirates can steal the license key, but:
- They can't use the same key on many servers (you bind it to specific IPs).
- You can revoke the key at any time, and it stops working immediately (via heartbeat).
- You can see exactly which servers are using your plugin.
- You control updates and can enforce version checks.
It's not perfect (nothing ever is), but it's orders of magnitude better than local checks.
Implementing License Verification in Your Plugin
Let's look at how you'd actually implement this in a Paper/Spigot plugin using ILicence, a dedicated licensing API for Minecraft plugins.
Adding the SDK to Your Project
First, add the ILicence SDK to your Maven pom.xml:
<repository>
<id>irixiagroup-releases</id>
<url>https://repo.irixiagroup.ch/releases</url>
</repository>
<dependency>
<groupId>ch.irixiagroup</groupId>
<artifactId>ilicence-sdk</artifactId>
<version>1.3.0</version>
</dependency>Verifying in onEnable()
In your plugin's onEnable() method, call ILicence to verify:
@Override
public void onEnable() {
// Verify license at startup
boolean isValid = ILicence.verify(
this,
getConfig().getString("license-key"),
"super-items" // Your plugin slug
);
if (!isValid) {
getLogger().severe("Invalid license. Plugin disabled.");
Bukkit.getPluginManager().disablePlugin(this);
return;
}
// Continue with normal plugin startup
getLogger().info("License verified. Starting up...");
// ... register commands, listeners, etc
}Understanding the Builder Pattern
For more control, ILicence supports a builder pattern:
ILicenceBuilder builder = ILicence.builder()
.plugin(this)
.licenseKey(getConfig().getString("license-key"))
.pluginSlug("super-items")
.enableHeartbeat(true) // Re-verify every 30 minutes
.heartbeatInterval(30) // In minutes
.gracePeriodMinutes(1440) // 24 hours grace if API is down
.cacheEnabled(true); // Cache locally with SHA-256 integrity
ILicenceResponse response = builder.verify();
if (!response.isValid()) {
getLogger().severe("Invalid license: " + response.getReason());
Bukkit.getPluginManager().disablePlugin(this);
return;
}Heartbeat: Continuous Verification
The heartbeat feature periodically re-verifies your license with the API. This catches revoked keys in real time:
- Enabled by default: Heartbeat pings the API every 30 minutes.
- Grace period: If the API is temporarily down, the plugin continues running for 24 hours (configurable).
- Revocation: If you revoke a license in your dashboard, it stops working on the next heartbeat.
Handling License Expiration
If you want time-limited licenses, you can check the expiration date:
ILicenceResponse response = ILicence.builder()
.plugin(this)
.licenseKey(licenseKey)
.pluginSlug("super-items")
.verify();
if (response.isValid()) {
long expiresAt = response.getExpirationTime();
if (expiresAt > 0 && System.currentTimeMillis() > expiresAt) {
getLogger().severe("License expired on " + new Date(expiresAt));
Bukkit.getPluginManager().disablePlugin(this);
return;
}
}Server IP Binding
When you issue a license key, you can bind it to specific server IPs. The verification API automatically enforces this:
// In your dashboard, when creating a license:
// License key: ABC123-DEF456
// Plugin: super-items
// Allowed IPs: 192.168.1.50 (max 1 server)
// On the server 192.168.1.50, this will verify:
ILicence.verify(this, "ABC123-DEF456", "super-items"); // OK
// On any other server IP, it will fail
// Pirates can steal the key, but it won't work on their serversBest Practices for Plugin Protection
1. Obfuscate AND Use an API (Defense in Depth)
Use both obfuscation (ProGuard) and a licensing API. Obfuscation slows down casual crackers. An API stops the determined ones. Together, they provide defense in depth:
- Obfuscate your entire plugin with ProGuard.
- Obfuscate the SDK call so the ILicence.verify() is hard to spot.
- Still verify with the API—obfuscation is not protection.
2. Spread Checks Throughout Your Code
Don't just check the license in onEnable(). Add verification checks in key features:
public void onCustomItemCreate(Player player, ItemStack item) {
if (!ILicence.isValid(this)) {
player.sendMessage("License expired. Feature disabled.");
return;
}
// Continue with feature logic
}This makes it harder for someone to modify the plugin by just patching out a single check.
3. Use Heartbeat for Real-Time Revocation
Enable heartbeat verification. If you need to revoke a license (e.g., a customer didn't pay), the plugin will stop working on the next heartbeat. Without heartbeat, pirates keep using stolen keys forever.
4. Set Server IP Limits
When issuing licenses, restrict them to a specific number of servers. A single-server license is harder to share than a license for 10 servers.
5. Monitor Your Dashboard
Your licensing dashboard shows you:
- Which servers are running your plugin.
- How many licenses are active.
- Verification success rates.
- Which keys have been revoked.
Use this data to spot suspicious activity (e.g., a key being used from 10 different IPs).
6. Document Your Licensing Terms
Be clear with your customers:
- "This plugin is licensed, not sold. You may not distribute it."
- "Sharing license keys is against the license agreement."
- "We may revoke licenses for non-payment or terms violations."
Make it clear that piracy has consequences. Some server owners respect that.
Getting Started with ILicence
Now that you understand how plugin licensing works, here's how to actually protect your plugins with ILicence:
Step 1: Create an Account
Sign up at ilicence.ch. The free tier includes 1 plugin and up to 10 license keys.
Step 2: Register Your Plugin
In your dashboard, create a new plugin entry. You'll get a plugin slug (e.g., super-items) that you use in your code.
Step 3: Add the SDK to Your Project
Add the ILicence SDK dependency to your pom.xml (shown above) and update your plugin's onEnable() method.
Step 4: Generate License Keys
Use the dashboard to generate keys for your customers. Optionally bind them to specific server IPs or set expiration dates.
Step 5: Deploy and Monitor
Release your plugin. When servers start up and use your license key, you'll see verification events in your dashboard. Monitor for suspicious activity and revoke keys as needed.
Read the Docs
For a complete technical reference, including API endpoints, SDK configuration, and deployment guides, visit the ILicence documentation.
Summary
Plugin piracy is a real problem, but it's not unsolvable. The key insights:
- Local checks (obfuscation, hardcoded UUIDs) don't work. Pirates control the user's machine.
- A remote licensing API puts the truth about licenses on your server, where pirates can't tamper with it.
- Combine API verification with heartbeat re-verification and server IP binding for strong protection.
- Use obfuscation + API together (defense in depth), not instead of an API.
- Spread verification checks throughout your code and monitor your dashboard.
Building a licensing system from scratch is time-consuming. ILicence handles the hard parts—API infrastructure, verification, heartbeat, caching—so you can focus on building great plugins. Start for free today.
Ready to protect your plugins?
Start protecting your Minecraft plugins in minutes. Free tier includes 1 plugin and 10 license keys.