Skip to main content
Obfuscation

How to Obfuscate Python Code: The 2026 Complete Guide

Pyobfuscate Team··9 min read
Quick Answer

To obfuscate Python code, transform it so it still runs but is hard to read or reverse: rename identifiers, encrypt string and number literals, and restructure logic at the AST level, then optionally compile it to a binary (.pyd/.so/.exe). Cosmetic tricks like base64 + exec() only *hide* code — they reverse in two lines. Real protection layers AST obfuscation (use our free Python Obfuscator) with compilation.

Python is shipped as source. Even when you distribute compiled .pyc bytecode, freely available decompilers reconstruct near-original code — comments gone, but logic, names, and strings intact. If you sell a script, protect an algorithm, or hide an API key, plain Python gives your logic away.

Obfuscation doesn't make code *un*-readable — nothing does, since the interpreter must ultimately run it. What it does is raise the cost of reverse engineering high enough that it isn't worth an attacker's time. This guide walks the three real levels of Python obfuscation, from the weak tricks you should *not* rely on to the layered approach that actually holds up — every claim tested on Python 3.14.

What obfuscation can (and cannot) do

Obfuscation is deterrence, not encryption. The Python runtime needs to execute your code, so a determined attacker with enough time can always recover *behaviour*. The goal is to make that so slow and painful that casual copying, key extraction, and license bypass stop being practical.

That means the right question isn't "is this unbreakable?" (nothing is) but "how many hours of skilled effort does this add?" Judge every technique below on that scale.

Level 1: The weak tricks (base64, exec, marshal)

The most-shared "obfuscation" snippets on the internet just encode your source and exec() it at runtime. Here's a tiny licensing check we'll use throughout:

def check_license(key):
    valid = "PYOB-2026-PRO"
    if key == valid:
        return "Access granted"
    return "Access denied"

print(check_license("PYOB-2026-PRO"))
secret.py — the original

Wrap it in base64 + exec() and it still runs identically:

import base64
exec(base64.b64decode("ZGVmIGNoZWNrX2xpY2Vuc2Uoa2V5KToKICAgIHZhbGlk..."))
base64 + exec — looks scrambled

It *looks* protected. It isn't. Because the payload is just encoded — not transformed — anyone can decode it back to your exact source in two lines, and your secret string reappears verbatim:

import base64
print(base64.b64decode("ZGVmIGNoZWNr...").decode())
# ->  valid = "PYOB-2026-PRO"        <-- your secret, right back
Reversed in 2 lines — tested, real output

The marshal variant (dumping a compiled code object instead of text) is marginally better — you get bytecode rather than source — but that bytecode still decompiles, and the loader is a neon sign to any reverser. These tricks stop a curious teammate, nothing more.

Never rely on `base64`/`exec`/`marshal` alone. They add minutes of effort, not hours. Antivirus engines also flag exec(b64decode(...)) as a malware pattern — so this can get your legitimate app quarantined.

Level 2: AST-level obfuscation (the real starting point)

Proper obfuscation rewrites the program's Abstract Syntax Tree — the structured representation Python parses your code into — and emits new, valid Python that behaves the same but is genuinely hard to read. The four transforms that matter:

TransformWhat it doesWhy it hurts a reverser
Identifier renamingTurns check_license, valid into l1lI, Il1lRemoves all semantic hints from names
Literal encryptionReplaces "PYOB-2026-PRO" with a decrypt-at-runtime callStrings no longer grep-able in the file
Control-flow flatteningRewrites if/for into a dispatched state machineExecution order stops matching the text
Junk / opaque codeInjects dead branches that never change outputBuries real logic in noise

Doing this by hand is error-prone. Our free **Python Obfuscator** applies renaming, string/number encryption and optional compression at the AST level, in your browser, and returns standalone Python — no runtime to ship. Unlike the Level 1 tricks, the "PYOB-2026-PRO" string is encrypted, so it never appears in the output.

AST obfuscation preserves behaviour but changes structure — always run your test suite against the obfuscated output before shipping. Our tool keeps output runnable so you can diff results instantly.

Level 3: Compilation (.pyd / .so / .exe)

The strongest, most practical layer is to stop shipping Python text at all. Compiling to a native artifact means there's no .py to read and no straightforward bytecode to decompile:

ApproachOutputReverse difficulty
Cython / .pyd / .soCompiled C extension moduleHigh — native machine code, no bytecode
NuitkaTrue compiled binary of the whole appHigh — compiles Python to C
PyInstaller + obfuscationBundled .exe (bytecode inside)Medium — extractable, so obfuscate first

PyInstaller alone is a *packager*, not protection — the bytecode can be extracted from the bundle. That's why you obfuscate first, then package. See Convert Python to EXE and protect it, or go straight to a single encrypted build with Obfuscator PRO (.pyd).

A layered strategy that actually holds up

No single technique is enough; strength comes from stacking them so each layer the attacker peels reveals another. A practical, free-to-strong ladder:

LayerTechniqueEffortProtection
1AST obfuscation (rename + encrypt strings)Low (paste & click)Good
2+ control-flow flattening / junk codeLowBetter
3+ compile to .pyd/.so or .exeMediumStrong
4+ runtime licensing / hardware lock (commercial)HighStrongest

For most people, Layers 1–3 — all achievable free on this site — move you from "copy-pasteable in 30 seconds" to "not worth the afternoon it would take." That's a win.

How to obfuscate your Python code for free

The fastest path, no install or signup:

1. Open the free Python Obfuscator. 2. Paste your source into the input box. 3. Keep Rename and Encrypt on (add Compress for a denser single-line output). 4. Click Obfuscate Code and copy the standalone result. 5. For a stronger build, feed that output into py-to-exe or use Obfuscator PRO (.pyd).

Everything runs in your browser — your source never leaves your machine, which matters when the whole point is secrecy.

Obfuscate your Python code now — free

AST-level renaming and string encryption, in your browser. No install, no signup, standalone output.

Open the Python Obfuscator

Free tools mentioned here

Frequently asked questions

Is Python obfuscation reversible?

Partly — obfuscation is deterrence, not encryption. Because the interpreter must run the code, a determined attacker can eventually recover its behaviour. Good obfuscation makes that so time-consuming it stops being worthwhile, especially when you layer AST obfuscation with compilation.

Is base64 or marshal enough to protect Python code?

No. base64 + exec() only encodes your source; it decodes back to the exact original in two lines, and any secret strings reappear verbatim. marshal ships bytecode that still decompiles. Use AST-level obfuscation instead, ideally plus compilation.

Does obfuscation slow Python down?

AST renaming and string encryption add negligible overhead. Heavy control-flow flattening or per-call literal decryption can add measurable cost in hot loops, so benchmark. Compiling to .pyd/.so or a Nuitka binary usually makes code faster, not slower.

Can I obfuscate Python code for free?

Yes. Our Python Obfuscator does AST-level renaming and string/number encryption free in the browser with no signup, and returns standalone Python. You can then compile it to an .exe or .pyd for stronger protection.

Does shipping .pyc files protect my source?

No. Compiled .pyc bytecode decompiles back to near-original source (logic, names and strings intact) with free tools. Treat .pyc as convenience, not protection — obfuscate the source and/or compile to a native module.