Skip to content

How to Override Claude Code's System Prompt (Complete Guide 2026)

The Problem: System Prompt Only Appends

When I tried to customize Claude Code’s behavior with a custom system prompt, I discovered something frustrating:

My attempt to override the system prompt
claude --system-prompt-file custom_instructions.md

I expected my custom instructions to replace Claude Code’s built-in system prompt. Instead, my instructions were appended to the end.

Here’s what actually happens:

--system-prompt-file behavior
[Claude Code's internal system prompt: ~10,000+ tokens]
+
[Your custom_instructions.md content]
=
[Combined prompt sent to Claude]

The problem: Claude Code has a sophisticated internal system prompt that defines:

  • Tool usage patterns (Read, Edit, Write, Bash, Glob, Grep)
  • Agent behaviors and decision-making logic
  • Safety guidelines and permission handling
  • MCP server integration rules

My custom instructions couldn’t override these core behaviors because they arrived at the weakest position in the prompt hierarchy.

The Prompt Hierarchy in Claude Code

I found a Reddit thread where developers were investigating this exact issue. One user discovered:

“Claude Code’s source confirms the system prompt problem - claude --system-prompt-file x appears only to append, not to replace, the actual system prompt seen by the Claude model.”

The hierarchy looks like this:

Claude Code Prompt Hierarchy
┌─────────────────────────────────────────────────────────────────────┐
│ Level 1: Internal System Prompt │
│ Priority: Highest │
│ Can Override: NO (except via proxy workaround) │
│ Contains: Tool definitions, agent behaviors, safety rules │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ Level 2: --system-prompt-file │
│ Priority: Medium │
│ Can Override: Only appends to Level 1 │
│ Contains: Your custom instructions │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ Level 3: Managed/Project Settings │
│ Priority: Medium-High │
│ Can Override: Constraints only, not core behaviors │
│ Contains: Project-specific rules │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ Level 4: CLAUDE.md │
│ Priority: Low │
│ Can Override: Context injection only │
│ Contains: Project context and preferences │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ Level 5: User Message │
│ Priority: Lowest │
│ Can Override: Request framing only │
│ Contains: Your actual prompt │
└─────────────────────────────────────────────────────────────────────┘

The Reddit thread revealed a key insight:

“CLAUDE.md is structurally subordinate to the system[] API parameter… arrives in a contradictory frame… occupies the weakest position in the prompt hierarchy.”

This explains why my CLAUDE.md couldn’t override core behaviors either.

The Solution: ANTHROPIC_BASE_URL Workaround

The same Reddit thread mentioned an unexpected workaround:

“Interestingly, setting ANTHROPIC_BASE_URL to a non-Anthropic service actually works correctly to override.”

This works because Claude Code has different behavior when connecting to Anthropic’s API vs. third-party endpoints.

How It Works

When Claude Code connects to Anthropic’s API directly, it injects its internal system prompt before your custom instructions. But when routing through a proxy or alternative endpoint, this injection behavior changes.

Here’s the setup:

Setting ANTHROPIC_BASE_URL to a proxy
export ANTHROPIC_BASE_URL=http://localhost:8080/v1
claude --system-prompt-file my_custom_prompt.md

The proxy intercepts requests and can:

  1. Strip Claude Code’s internal system prompt
  2. Inject your custom system prompt
  3. Forward the request to Anthropic’s API

Building a Simple Proxy

I built a minimal proxy to test this:

proxy.py - Minimal proxy for system prompt override
from flask import Flask, request, Response
import requests
import os
app = Flask(__name__)
# Your custom system prompt
CUSTOM_SYSTEM_PROMPT = """
You are a specialized code assistant focused on Python development.
Always use type hints.
Prefer functional programming patterns.
Never use global state.
"""
ANTHROPIC_API_URL = "https://api.anthropic.com/v1/messages"
@app.route('/v1/messages', methods=['POST'])
def proxy():
# Get the request body
body = request.get_json()
# Override the system prompt
body['system'] = CUSTOM_SYSTEM_PROMPT
# Forward to Anthropic
headers = {
'Content-Type': 'application/json',
'x-api-key': os.environ.get('ANTHROPIC_API_KEY'),
'anthropic-version': '2023-06-01'
}
response = requests.post(
ANTHROPIC_API_URL,
json=body,
headers=headers
)
return Response(
response.content,
status=response.status_code,
headers=dict(response.headers)
)
if __name__ == '__main__':
app.run(port=8080)

Then I started the proxy and connected Claude Code:

Running the proxy
# Terminal 1: Start the proxy
export ANTHROPIC_API_KEY=your-key-here
python proxy.py
# Terminal 2: Connect Claude Code
export ANTHROPIC_BASE_URL=http://localhost:8080/v1
claude --system-prompt-file custom_instructions.md

Now my custom system prompt actually replaces the built-in one.

Testing the Override

To verify the override worked, I asked Claude what system prompt it received:

Before override (using --system-prompt-file directly)
Me: What system prompt are you operating under?
Claude: I'm Claude, an AI assistant from Anthropic. I have access to various tools
including Read, Edit, Write, Bash, Glob, and Grep. I'm designed to help with
software development tasks like...
[Continues with standard Claude Code behavior]
After override (using ANTHROPIC_BASE_URL proxy)
Me: What system prompt are you operating under?
Claude: I am a specialized code assistant focused on Python development.
I always use type hints, prefer functional programming patterns,
and never use global state.
[Follows only my custom prompt]

The difference is clear: the proxy successfully replaced the system prompt.

Why This Matters

Understanding the prompt hierarchy and override methods matters for several use cases:

1. Custom Agent Behaviors

If you want Claude Code to follow specific coding standards or workflows that conflict with its default behavior, you need to override the system prompt.

Use case: Domain-specific assistant
Without override: Claude Code follows general software development patterns
With override: Claude Code follows your organization's specific patterns

2. Security and Compliance

Organizations may need to ensure Claude Code follows specific security guidelines:

Use case: Security compliance
Without override: Standard safety guidelines
With override: Organization-specific security protocols, data handling rules

3. Testing with Alternative Models

The proxy approach also enables testing with alternative models:

Proxy for alternative models
@app.route('/v1/messages', methods=['POST'])
def proxy():
body = request.get_json()
body['system'] = CUSTOM_SYSTEM_PROMPT
# Route to alternative API instead of Anthropic
response = requests.post(
"https://alternative-api.com/v1/chat",
json=body,
headers=custom_headers
)
return Response(response.content, ...)

Limitations and Trade-offs

The ANTHROPIC_BASE_URL workaround isn’t perfect:

AspectDirect ConnectionProxy Override
System prompt controlAppends onlyFull override
Tool definitionsAutomaticMust handle manually
Safety featuresBuilt-inMust reimplement
Setup complexityNoneRequires proxy
PerformanceOptimalAdditional latency
Official supportYesNo (workaround)

When using the proxy approach, you may need to:

  1. Handle tool definitions manually if stripped
  2. Reimplement any safety features you want to keep
  3. Maintain the proxy infrastructure

The Reason: Why Claude Code Appends

Claude Code’s append-only behavior exists for practical reasons:

  1. Tool Integration: The internal system prompt defines how Claude uses tools (Read, Edit, Write, Bash, etc.). Stripping this would break tool functionality.

  2. Safety Guardrails: Built-in safety guidelines prevent dangerous operations. Removing them could allow harmful actions.

  3. Consistent Behavior: Anthropic wants Claude Code to behave predictably across different users and projects.

The Reddit thread captured this tension:

“CLAUDE.md is structurally subordinate to the system[] API parameter… arrives in a contradictory frame… occupies the weakest position in the prompt hierarchy.”

The design prioritizes consistency and safety over customization. The ANTHROPIC_BASE_URL workaround exploits a side-effect: different handling for non-Anthropic endpoints.

Summary

In this post, I showed how to fully override Claude Code’s system prompt. The key points:

  1. The problem: --system-prompt-file only appends, never replaces
  2. The hierarchy: Internal system prompt has highest priority, CLAUDE.md has lowest
  3. The solution: Setting ANTHROPIC_BASE_URL to a proxy endpoint enables full override
  4. The trade-off: More control but more complexity and responsibility

For most users, appending via --system-prompt-file is sufficient. But when you need complete control over Claude Code’s behavior, the proxy approach is the only proven method.

The Reddit community discovered this through experimentation: “Multiple users tested this approach and confirmed that routing through a proxy service gives complete control over the system prompt.”

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments