Skip to main content
Snort rules have the following general structure:
action proto source dir dest ( option:value; option:value; ... )
For example:
alert tcp any any -> 192.168.1.1 80 ( msg:"A ha!"; content:"attack"; sid:1; )
Service-based rules can omit port information entirely:
alert http
(
    msg:"Gotcha!";
    flow:established, to_server;
    http_uri:"attack";
    sid:2;
)
Snort 3 rules allow arbitrary whitespace, including multi-line formatting. Use # for end-of-line comments, #begin/#end blocks for multi-line comments, or /* */ for C-style inline comments.

Rule Actions (Header)

The action is the first word in a rule and determines what happens when the rule matches.
ActionModeDescription
alertAnyGenerate an alert event and log the triggering packet.
logAnyLog the packet without generating an alert.
passAnyIgnore the packet; no alert, no logging.
dropInline IPSDrop the packet silently.
rejectInline IPSDrop the packet and send a TCP reset (or ICMP unreachable for UDP).
rewriteInline IPSReplace matched content and reinject the modified packet.
# IDS mode: generate an alert
alert tcp any any -> any 80 ( msg:"HTTP traffic"; sid:1000; )

# Inline IPS mode: drop and reset
reject tcp any any -> any 23 ( msg:"Telnet blocked"; sid:1001; )
drop, reject, and rewrite only take effect when Snort is running in inline mode (e.g., using the afpacket DAQ with a bridged pair). In IDS mode they behave like alert.

General Options

These options provide metadata and identification for the rule.
OptionSyntaxDescription
msgmsg:"<text>";Human-readable alert message. Appears in alert output.
sidsid:<n>;Signature ID. Unique within a generator ID.
gidgid:<n>;Generator ID. Defaults to 1 for text rules, 3 for SO rules.
revrev:<n>;Rule revision number. Increment when the rule is updated.
classtypeclasstype:<name>;Classification name. Must match an entry in classifications.
prioritypriority:<n>;Alert priority. Overrides the priority set by classtype.
metadatametadata:<key value>, ...;Arbitrary key-value pairs (unquoted, comma-separated).
referencereference:<scheme>,<id>;Link to an external vulnerability database entry.
remrem:"<comment>";Rule comment that is carried with the rule itself.
alert tcp any any -> any 80
(
    msg:"Example HTTP rule";
    gid:1;
    sid:9001;
    rev:3;
    classtype:web-application-attack;
    priority:1;
    reference:cve,2021-12345;
    metadata:affected_product Web_Server, created_at 2021_10_01;
    rem:"Detects suspicious GET requests";
)
Rules are identified in the format gid:sid:rev. Internal Snort components use GIDs in the 100s range (e.g., the decoder is GID 116). List all built-in GIDs with snort --list-gids.

Detection Options

content

Matches a literal byte sequence in the current buffer. Can be a text string or a mix of text and hex bytes.
content:"<string>";
content:"|HH HH HH|";          # hex bytes
content:"text|0D 0A|more";    # mixed
Content sub-options (follow the content option, separated by commas or as additional options):
Sub-optionSyntaxDescription
nocasenocase;Case-insensitive match.
rawbytesrawbytes;Match on raw bytes, bypassing any normalisation.
depthdepth:<n>;Search only the first n bytes of the buffer (from the start or from offset).
offsetoffset:<n>;Start searching at byte n from the beginning of the buffer.
distancedistance:<n>;Start searching n bytes after the end of the previous match (relative).
withinwithin:<n>;Require this match to occur within n bytes of the previous match.
fast_patternfast_pattern;Designate this content as the fast pattern for rule grouping.
# Match GET with case insensitivity
content:"GET"; nocase;

# Anchored match: look in the first 100 bytes
content:"User-Agent:"; depth:100;

# Relative match: within 20 bytes after a prior match
content:"Mozilla"; distance:0; within:20;

# Explicit fast pattern selection
content:"EvilPayload"; fast_pattern;
Snort automatically selects the longest content as the fast pattern. Override with fast_pattern on the most selective (unique) content for best performance.

pcre

Matches using a Perl-compatible regular expression.
pcre:"/<pattern>/<flags>";
Common flags: i (case-insensitive), s (dot matches newline), m (multiline), U (match URI buffer), R (relative to last match).
pcre:"/SELECT.+FROM/i";
pcre:"/^POST\s+\//mR";

regex

Matches using a Hyperscan-accelerated regular expression. Requires Snort built with Hyperscan support.
regex:"<pattern>"; [flags]
regex:"evil|malicious"; nocase;

isdataat

Verifies that data exists at a specific position in the current buffer. Useful as a bounds check.
isdataat:<pos>[, relative];
isdataat:!<pos>;              # assert there is NOT data at pos
# Confirm at least 50 bytes remain after the last match
isdataat:50, relative;

dsize

Matches on the size of the packet payload.
dsize:<range>;
dsize:100;           # exactly 100 bytes
dsize:>100;          # more than 100 bytes
dsize:50<>200;       # between 50 and 200 bytes

Flow Options

flow

Controls which side of a connection the rule applies to and what session state is required.
flow:<option>[, <option>];
OptionDescription
establishedMatch only on established TCP sessions.
to_server / from_clientMatch traffic from client to server.
to_client / from_serverMatch traffic from server to client.
statelessMatch regardless of flow state (no stream required).
no_streamDo not match on rebuilt stream packets.
only_streamOnly match on rebuilt stream packets (not raw).
# Client request on an established session
flow:established, to_server;

# Server response
flow:established, to_client;

flowbits

Sets or tests per-flow boolean flags, enabling stateful multi-rule correlation.
flowbits:<action>[, <name>];
ActionDescription
set,<name>Set the named flag for this flow.
isset,<name>Match only if the named flag is set.
unset,<name>Clear the named flag.
isnotset,<name>Match only if the named flag is not set.
toggle,<name>Toggle the named flag.
noalertSuppress the alert for this rule (used to set bits without alerting).
# Rule 1: detect login attempt and tag the flow
alert tcp any any -> any 21
(
    msg:"FTP login attempt";
    flow:established, to_server;
    content:"USER"; nocase;
    flowbits:set, ftp.login;
    flowbits:noalert;
    sid:2001;
)

# Rule 2: alert only if login was previously seen
alert tcp any any -> any 21
(
    msg:"FTP password after login";
    flow:established, to_server;
    content:"PASS"; nocase;
    flowbits:isset, ftp.login;
    sid:2002;
)

service

Restates the rule’s associated service, placing it in the corresponding service rule group. Makes the rule service-based rather than port-based.
service:<name>[, <name>];
service:http;
service:smtp, imap;

HTTP Buffer Selectors

These sticky buffer options set the detection cursor to a specific HTTP field. Subsequent content and pcre options match within that buffer.
OptionDescription
http_uriNormalised request URI.
http_raw_uriRaw (un-normalised) request URI.
http_methodHTTP request method (e.g., GET, POST).
http_headerNormalised HTTP request or response headers.
http_raw_headerRaw HTTP headers.
http_client_bodyHTTP request body (POST data).
http_stat_codeHTTP response status code.
http_stat_msgHTTP response status message.
file_dataDecoded file content (HTTP response body, email attachments, etc.). Supports stateful evaluation across packet boundaries.
pkt_dataRaw TCP reassembled payload bytes. Supports stateful evaluation across packet boundaries.
alert http
(
    msg:"SQL injection attempt in URI";
    flow:established, to_server;
    http_uri;
    content:"UNION SELECT"; nocase;
    pcre:"/UNION\s+SELECT/Ui";
    sid:3001;
)

alert http
(
    msg:"Suspicious PHP in file body";
    flow:established, to_client;
    file_data;
    content:"<?php"; nocase;
    sid:3002;
)
Stateful evaluation is supported for pkt_data and file_data. If a match spans packet boundaries, rule evaluation pauses and resumes when more data arrives.

Byte Rule Options

byte_test

Extracts a numeric value from the packet and compares it against a constant.
byte_test:<bytes>, <operator>, <value>, <offset>[, relative][, <endian>][, <type>];
ParameterDescription
<bytes>Number of bytes to extract (1–10).
<operator>Comparison: =, !=, <, >, <=, >=, &, ^.
<value>Value to compare against.
<offset>Byte offset from the start of the buffer (or from last match if relative).
# Check if the 2-byte value at offset 0 equals 0x0200
byte_test:2, =, 512, 0, big;

# Relative test: 4 bytes after last content match, value > 1000
byte_test:4, >, 1000, 0, relative;

byte_jump

Moves the detection cursor by an amount extracted from the packet. Useful for navigating length-prefixed fields.
byte_jump:<bytes>, <offset>[, relative][, multiplier <n>][, big|little][, align];
# Extract a 2-byte length at offset 0, jump forward that many bytes
byte_jump:2, 0;

# Then match content relative to the new cursor position
content:"DATA"; distance:0;

byte_extract

Extracts a value from the packet and stores it in a named variable for use in subsequent options.
byte_extract:<bytes>, <offset>, <name>[, relative][, big|little];
# Extract a 2-byte length into variable 'len'
byte_extract:2, 0, len;

# Use the variable as a within constraint
content:"MARKER";
within:len;

byte_math

Performs arithmetic on an extracted byte value and stores the result in a named variable.
byte_math:<bytes>, <offset>, <operator>, <operand>, result <name>[, relative][, big|little];
# Extract 2 bytes, multiply by 4, store as 'adjusted_len'
byte_math:2, 0, *, 4, result adjusted_len;

Threshold / Suppression Options

threshold (in-rule)

Limits how often a rule can fire for a given source or destination within a time window. Embedded in the rule itself.
threshold: type <limit|threshold|both>, track <by_src|by_dst>, count <n>, seconds <n>;
typeBehaviour
limitLog only the first count events in the time window.
thresholdLog every countth event.
bothLog the first event and then every countth after that.
alert tcp any any -> any 22
(
    msg:"SSH brute force";
    flow:established, to_server;
    content:"SSH-";
    threshold: type both, track by_src, count 5, seconds 60;
    sid:4001;
)

detection_filter

Prevents a rule from firing until the rate threshold is met. The rule must match count times within seconds before the first alert is generated.
detection_filter: track <by_src|by_dst>, count <n>, seconds <n>;
alert tcp any any -> any 25
(
    msg:"SMTP connection flood";
    flow:stateless;
    detection_filter: track by_src, count 100, seconds 1;
    sid:4002;
)

Complete Example Rule

The following rule combines multiple option categories to detect a specific HTTP-based exploit pattern:
alert http
(
    msg:"Exploit Kit - Malicious PDF via HTTP";

    -- flow control
    flow:established, to_client;

    -- HTTP buffer
    file_data;

    -- content detection
    content:"%PDF-"; depth:1024;
    content:"/JavaScript"; distance:0;
    pcre:"/\/JavaScript\s*>>/";

    -- payload size check
    dsize:>512;

    -- rate limiting
    detection_filter: track by_dst, count 3, seconds 60;

    -- metadata
    classtype:file-format;
    priority:1;
    reference:cve,2010-0188;
    sid:9999;
    rev:2;
)
There are several ways to load rules into Snort:
  1. Configuration file — set ips.include or ips.rules in your Lua config.
  2. Command line — use -R <file> to load an additional rules file.
  3. Stdin — use --stdin-rules with shell redirection.
  4. Inline Lua — use --lua to pass a rule as a string.
  5. Include statements — use include inside a rules file to load other rules files.
# Load rules from a file
snort -c snort.lua -R cool.rules

# Load from stdin
snort -c snort.lua --stdin-rules < cool.rules

# Pass a single rule inline
snort -c snort.lua --lua 'ips = { rules = [[alert tcp any any -> any 80 (msg:"test"; sid:1;)]] }'
Snort compiles rules into multi-pattern search engines (MPSE) grouped by protocol, port, and service. Understanding how rules are grouped affects detection performance:
  • A port-based rule (alert tcp any 80 -> any any) is added to a port group; both protocol and port are evaluated.
  • A service-based rule (e.g., uses http_uri, service:http, or alert http) is added to a service group; port evaluation is skipped when the service is identified.
  • The fast pattern is chosen automatically as the longest content; use fast_pattern to designate a more selective one.
  • Negated contents, and contents with non-zero offset/depth that are also case-sensitive, are not eligible for fast pattern selection.
Rules are referenced as gid:sid:rev:
GIDDescription
1Standard text rules (default).
3Shared-object (SO) rules.
100+Internal Snort components (e.g., decoder = 116).
# List all internal GIDs
snort --list-gids

# List all built-in rule GID:SID pairs
snort --list-builtin