diff --git a/internal/constraints/cache.go b/internal/constraints/cache.go
index 7f56938..b546849 100644
--- a/internal/constraints/cache.go
+++ b/internal/constraints/cache.go
@@ -49,18 +49,25 @@ func (c *Cache) Build(
 	fieldDesc protoreflect.FieldDescriptor,
 	fieldConstraints *validate.FieldConstraints,
 	extensionTypeResolver protoregistry.ExtensionTypeResolver,
+	allowUnknownFields bool,
 	forItems bool,
 ) (set expression.ProgramSet, err error) {
 	constraints, done, err := c.resolveConstraints(
 		fieldDesc,
 		fieldConstraints,
-		extensionTypeResolver,
 		forItems,
 	)
 	if done {
 		return nil, err
 	}
 
+	if err = reparseUnrecognized(extensionTypeResolver, constraints); err != nil {
+		return nil, fmt.Errorf("error reparsing message: %w", err)
+	}
+	if !allowUnknownFields && len(constraints.GetUnknown()) > 0 {
+		return nil, errors.NewCompilationErrorf("unknown rules in %s", constraints.Descriptor().FullName())
+	}
+
 	env, err = c.prepareEnvironment(env, fieldDesc, constraints, forItems)
 	if err != nil {
 		return nil, err
@@ -103,7 +110,6 @@ func (c *Cache) Build(
 func (c *Cache) resolveConstraints(
 	fieldDesc protoreflect.FieldDescriptor,
 	fieldConstraints *validate.FieldConstraints,
-	extensionTypeResolver protoregistry.ExtensionTypeResolver,
 	forItems bool,
 ) (rules protoreflect.Message, done bool, err error) {
 	constraints := fieldConstraints.ProtoReflect()
@@ -124,10 +130,6 @@ func (c *Cache) resolveConstraints(
 		return nil, true, nil
 	}
 	rules = constraints.Get(setOneof).Message()
-	// Reparse unrecognized fields so that we get dynamic extensions.
-	if err = reparseUnrecognized(extensionTypeResolver, rules); err != nil {
-		return nil, false, fmt.Errorf("error reparsing message: %w", err)
-	}
 	return rules, false, nil
 }
 
diff --git a/internal/constraints/cache_test.go b/internal/constraints/cache_test.go
index dc689b5..0c13412 100644
--- a/internal/constraints/cache_test.go
+++ b/internal/constraints/cache_test.go
@@ -101,7 +101,7 @@ func TestCache_BuildStandardConstraints(t *testing.T) {
 			require.NoError(t, err)
 			c := NewCache()
 
-			set, err := c.Build(env, test.desc, test.cons, protoregistry.GlobalTypes, test.forItems)
+			set, err := c.Build(env, test.desc, test.cons, protoregistry.GlobalTypes, false, test.forItems)
 			if test.exErr {
 				assert.Error(t, err)
 			} else {
diff --git a/internal/evaluator/builder.go b/internal/evaluator/builder.go
index 82447c4..a795097 100644
--- a/internal/evaluator/builder.go
+++ b/internal/evaluator/builder.go
@@ -38,6 +38,7 @@ type Builder struct {
 	constraints           constraints.Cache
 	resolver              StandardConstraintResolver
 	extensionTypeResolver protoregistry.ExtensionTypeResolver
+	allowUnknownFields    bool
 	Load                  func(desc protoreflect.MessageDescriptor) MessageEvaluator
 }
 
@@ -53,6 +54,7 @@ func NewBuilder(
 	disableLazy bool,
 	res StandardConstraintResolver,
 	extensionTypeResolver protoregistry.ExtensionTypeResolver,
+	allowUnknownFields bool,
 	seedDesc ...protoreflect.MessageDescriptor,
 ) *Builder {
 	bldr := &Builder{
@@ -60,6 +62,7 @@ func NewBuilder(
 		constraints:           constraints.NewCache(),
 		resolver:              res,
 		extensionTypeResolver: extensionTypeResolver,
+		allowUnknownFields:    allowUnknownFields,
 	}
 
 	if disableLazy {
@@ -362,6 +365,7 @@ func (bldr *Builder) processStandardConstraints(
 		fdesc,
 		constraints,
 		bldr.extensionTypeResolver,
+		bldr.allowUnknownFields,
 		forItems,
 	)
 	if err != nil {
diff --git a/internal/evaluator/builder_test.go b/internal/evaluator/builder_test.go
index 82573a7..bdee0cd 100644
--- a/internal/evaluator/builder_test.go
+++ b/internal/evaluator/builder_test.go
@@ -34,7 +34,7 @@ func TestBuildCache(t *testing.T) {
 	env, err := celext.DefaultEnv(true)
 	require.NoError(t, err, "failed to construct CEL environment")
 	bldr := NewBuilder(
-		env, false, resolver.DefaultResolver{}, protoregistry.GlobalTypes,
+		env, false, resolver.DefaultResolver{}, protoregistry.GlobalTypes, false,
 	)
 	wg := sync.WaitGroup{}
 	for i := 0; i < 100; i++ {
diff --git a/validator.go b/validator.go
index 5618a7d..e02de71 100644
--- a/validator.go
+++ b/validator.go
@@ -79,6 +79,7 @@ func New(options ...ValidatorOption) (*Validator, error) {
 		cfg.disableLazy,
 		cfg.resolver,
 		cfg.extensionTypeResolver,
+		cfg.allowUnknownFields,
 		cfg.desc...,
 	)
 
@@ -110,6 +111,7 @@ type config struct {
 	desc                  []protoreflect.MessageDescriptor
 	resolver              StandardConstraintResolver
 	extensionTypeResolver protoregistry.ExtensionTypeResolver
+	allowUnknownFields    bool
 }
 
 // A ValidatorOption modifies the default configuration of a Validator. See the
@@ -198,3 +200,16 @@ func WithExtensionTypeResolver(extensionTypeResolver protoregistry.ExtensionType
 		c.extensionTypeResolver = extensionTypeResolver
 	}
 }
+
+// WithAllowUnknownFields specifies if the presence of unknown field constraints
+// should cause compilation to fail with an error. When set to false, an unknown
+// field will simply be ignored, which will cause constraints to silently not be
+// applied. This condition may occur if a predefined constraint definition isn't
+// present in the extension type resolver, or when passing dynamic messages with
+// standard constraints defined in a newer version of protovalidate. The default
+// value is false, to prevent silently-incorrect validation from occurring.
+func WithAllowUnknownFields(allowUnknownFields bool) ValidatorOption {
+	return func(c *config) {
+		c.allowUnknownFields = allowUnknownFields
+	}
+}