diff --git a/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/CloudFormationSubstitution.java b/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/CloudFormationSubstitution.java
index c007e480344..823c4b0cbe9 100644
--- a/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/CloudFormationSubstitution.java
+++ b/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/CloudFormationSubstitution.java
@@ -69,21 +69,13 @@ public byte getOrder() {
 
     @Override
     public ObjectNode updateNode(Context<? extends Trait> context, OpenApi openapi, ObjectNode node) {
-        if (!isDisabled(context)) {
+        if (!context.getConfig().getExtensions(ApiGatewayConfig.class).getDisableCloudFormationSubstitution()) {
             return node.accept(new CloudFormationFnSubInjector(PATHS)).expectObjectNode();
         }
 
         return node;
     }
 
-    private boolean isDisabled(Context<?> context) {
-        // Support the old name for backward compatibility.
-        return context.getConfig().getExtensions(ApiGatewayConfig.class).getDisableCloudFormationSubstitution()
-                || context.getConfig()
-                       .getExtensions()
-                       .getBooleanMemberOrDefault("apigateway.disableCloudFormationSubstitution");
-    }
-
     private static class CloudFormationFnSubInjector extends NodeVisitor.Default<Node> {
         private final Deque<String> stack = new ArrayDeque<>();
         private final List<String[]> paths;
diff --git a/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/Schema.java b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/Schema.java
index 237c6eeb264..13b035faa0f 100644
--- a/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/Schema.java
+++ b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/Schema.java
@@ -808,13 +808,13 @@ public Builder removeExtension(String key) {
         }
 
         /**
-         * Applies a "disableX" key to a schema builder.
+         * Disables a specific JSON schema property by name.
          *
-         * @param disableKey Disable key to apply (e.g., "disablePropertyNames").
+         * @param propertyName Property name to remove (e.g., "propertyNames").
          * @return Returns the builder.
          */
-        public Builder disableProperty(String disableKey) {
-            switch (disableKey) {
+        public Builder disableProperty(String propertyName) {
+            switch (propertyName) {
                 case "const":
                     return this.constValue(null);
                 case "default":
@@ -884,7 +884,7 @@ public Builder disableProperty(String disableKey) {
                 case "examples":
                     return this.examples(null);
                 default:
-                    LOGGER.warning("Unknown JSON Schema config 'disable' property: " + disableKey);
+                    LOGGER.warning("Unknown JSON Schema config 'disable' property: " + propertyName);
                     return this;
             }
         }
diff --git a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/OpenApiConfig.java b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/OpenApiConfig.java
index 768760fa358..780f4366c34 100644
--- a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/OpenApiConfig.java
+++ b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/OpenApiConfig.java
@@ -16,15 +16,20 @@
 package software.amazon.smithy.openapi;
 
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.logging.Logger;
 import software.amazon.smithy.jsonschema.JsonSchemaConfig;
 import software.amazon.smithy.model.node.Node;
+import software.amazon.smithy.model.node.NodeMapper;
+import software.amazon.smithy.model.node.ObjectNode;
 import software.amazon.smithy.model.shapes.ShapeId;
 import software.amazon.smithy.openapi.fromsmithy.OpenApiProtocol;
 import software.amazon.smithy.openapi.fromsmithy.Smithy2OpenApiExtension;
 import software.amazon.smithy.utils.ListUtils;
+import software.amazon.smithy.utils.StringUtils;
 
 /**
  * "openapi" smithy-build plugin configuration settings.
@@ -46,6 +51,26 @@ public enum HttpPrefixHeadersStrategy {
     /** The JSON pointer to where OpenAPI schema components should be written. */
     private static final String SCHEMA_COMPONENTS_POINTER = "#/components/schemas";
 
+    private static final Logger LOGGER = Logger.getLogger(OpenApiConfig.class.getName());
+    private static final Map<String, String> DEPRECATED_PROPERTY_RENAMES = new HashMap<>();
+
+    static {
+        DEPRECATED_PROPERTY_RENAMES.put("openapi.tags", "tags");
+        DEPRECATED_PROPERTY_RENAMES.put("openapi.supportedTags", "supportedTags");
+        DEPRECATED_PROPERTY_RENAMES.put("openapi.defaultBlobFormat", "defaultBlobFormat");
+        DEPRECATED_PROPERTY_RENAMES.put("openapi.keepUnusedComponents", "keepUnusedComponents");
+        DEPRECATED_PROPERTY_RENAMES.put("openapi.aws.jsonContentType", "jsonContentType");
+        DEPRECATED_PROPERTY_RENAMES.put("openapi.forbidGreedyLabels", "forbidGreedyLabels");
+        DEPRECATED_PROPERTY_RENAMES.put("openapi.onHttpPrefixHeaders", "onHttpPrefixHeaders");
+        // There was a typo in the docs, so might as well fix that here.
+        DEPRECATED_PROPERTY_RENAMES.put("openapi.ignoreUnsupportedTrait", "ignoreUnsupportedTraits");
+        DEPRECATED_PROPERTY_RENAMES.put("openapi.ignoreUnsupportedTraits", "ignoreUnsupportedTraits");
+        DEPRECATED_PROPERTY_RENAMES.put("openapi.substitutions", "substitutions");
+        // Cheating a little here, but oh well.
+        DEPRECATED_PROPERTY_RENAMES.put("apigateway.disableCloudFormationSubstitution",
+                                        "disableCloudFormationSubstitution");
+    }
+
     private ShapeId service;
     private ShapeId protocol;
     private boolean tags;
@@ -284,4 +309,49 @@ public List<String> getExternalDocs() {
     public void setExternalDocs(List<String> externalDocs) {
         this.externalDocs = Objects.requireNonNull(externalDocs);
     }
+
+    /**
+     * Creates an OpenApiConfig from a Node value.
+     *
+     * <p>This method first converts deprecated keys into their new locations and
+     * formats, and then uses the {@link NodeMapper} on the converted input
+     * object. Note that this class can be deserialized using a NodeMapper too
+     * since the NodeMapper will look for a static, public, fromNode method.
+     *
+     * @param input Input to deserialize.
+     * @return Returns the deserialized OpenApiConfig.
+     */
+    public static OpenApiConfig fromNode(Node input) {
+        NodeMapper mapper = new NodeMapper();
+        ObjectNode node = fixDeprecatedKeys(input.expectObjectNode());
+        OpenApiConfig config = new OpenApiConfig();
+        return mapper.deserializeInto(node, config);
+    }
+
+    private static ObjectNode fixDeprecatedKeys(ObjectNode node) {
+        ObjectNode mapped = node;
+
+        // Remove deprecated "openapi." prefixes from configuration settings.
+        for (Map.Entry<String, Node> entry : mapped.getStringMap().entrySet()) {
+            if (DEPRECATED_PROPERTY_RENAMES.containsKey(entry.getKey())) {
+                // Fixes specific renamed keys.
+                String rename = DEPRECATED_PROPERTY_RENAMES.get(entry.getKey());
+                LOGGER.warning("Deprecated `openapi` configuration setting found: " + entry.getKey()
+                               + ". Use " + rename + " instead");
+                mapped = mapped.withMember(rename, entry.getValue());
+                mapped = mapped.withoutMember(entry.getKey());
+            } else if (entry.getKey().startsWith("disable.")) {
+                // These are now added into the "disableFeatures" property.
+                String property = StringUtils.uncapitalize(entry.getKey().substring(8));
+                throw new OpenApiException("Unsupported `openapi` configuration setting found: " + entry.getKey()
+                                           + ". Add `" + property + "` to the `disableFeatures` property instead");
+            } else if (entry.getKey().startsWith("openapi.use.")) {
+                throw new OpenApiException(String.format(
+                        "The `%s` `openapi` plugin property is no longer supported. Use the "
+                        + "`disableFeatures` property instead to disable features.", entry.getKey()));
+            }
+        }
+
+        return mapped;
+    }
 }
diff --git a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/Smithy2OpenApi.java b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/Smithy2OpenApi.java
index 0bd968e977b..7cac415e736 100644
--- a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/Smithy2OpenApi.java
+++ b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/Smithy2OpenApi.java
@@ -15,12 +15,8 @@
 
 package software.amazon.smithy.openapi.fromsmithy;
 
-import java.util.Map;
-import java.util.logging.Logger;
 import software.amazon.smithy.build.PluginContext;
 import software.amazon.smithy.build.SmithyBuildPlugin;
-import software.amazon.smithy.model.node.Node;
-import software.amazon.smithy.model.node.NodeMapper;
 import software.amazon.smithy.model.node.ObjectNode;
 import software.amazon.smithy.model.shapes.ShapeId;
 import software.amazon.smithy.openapi.OpenApiConfig;
@@ -36,8 +32,6 @@
  */
 public final class Smithy2OpenApi implements SmithyBuildPlugin {
 
-    private static final Logger LOGGER = Logger.getLogger(Smithy2OpenApi.class.getName());
-
     @Override
     public String getName() {
         return "openapi";
@@ -47,23 +41,7 @@ public String getName() {
     public void execute(PluginContext context) {
         OpenApiConverter converter = OpenApiConverter.create();
         context.getPluginClassLoader().ifPresent(converter::classLoader);
-
-        // Remove deprecated "openapi." prefixes from configuration settings.
-        ObjectNode mapped = context.getSettings();
-        for (Map.Entry<String, Node> entry : mapped.getStringMap().entrySet()) {
-            if (entry.getKey().startsWith("openapi.")) {
-                String expected = entry.getKey().substring(8);
-                LOGGER.warning("Deprecated `openapi` configuration setting found: " + entry.getKey()
-                               + ". Use " + expected + " instead");
-                mapped = mapped.withoutMember(entry.getKey());
-                mapped = mapped.withMember(expected, entry.getValue());
-            }
-        }
-
-        NodeMapper mapper = new NodeMapper();
-        OpenApiConfig config = new OpenApiConfig();
-        mapper.deserializeInto(mapped, config);
-
+        OpenApiConfig config = OpenApiConfig.fromNode(context.getSettings());
         ShapeId shapeId = config.getService();
 
         if (shapeId == null) {
diff --git a/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/OpenApiConfigTest.java b/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/OpenApiConfigTest.java
new file mode 100644
index 00000000000..076c76a9cb8
--- /dev/null
+++ b/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/OpenApiConfigTest.java
@@ -0,0 +1,46 @@
+package software.amazon.smithy.openapi.fromsmithy;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import software.amazon.smithy.model.node.Node;
+import software.amazon.smithy.openapi.OpenApiConfig;
+import software.amazon.smithy.openapi.OpenApiException;
+
+public class OpenApiConfigTest {
+    @Test
+    public void throwsOnDisableProperties() {
+        Node disableTest = Node.objectNode().withMember("disable.additionalProperties", Node.from(true));
+
+        Exception thrown = Assertions.assertThrows(OpenApiException.class, () -> {
+            OpenApiConfig.fromNode(disableTest);
+        });
+
+        assertThat(thrown.getMessage(), containsString("disableFeatures"));
+    }
+
+    @Test
+    public void throwsOnOpenApiUseProperties() {
+        Node openApiUseTest = Node.objectNode().withMember("openapi.use.xml", Node.from(true));
+
+        Exception thrown = Assertions.assertThrows(OpenApiException.class, () -> {
+            OpenApiConfig.fromNode(openApiUseTest);
+        });
+
+        assertThat(thrown.getMessage(), containsString("disableFeatures"));
+    }
+
+    @Test
+    public void convertsExplicitlyMappedProperties() {
+        Node mappedTest = Node.objectNode()
+                .withMember("openapi.tags", Node.from(true))
+                .withMember("openapi.ignoreUnsupportedTraits", Node.from(true));
+        OpenApiConfig config = OpenApiConfig.fromNode(mappedTest);
+
+        assertThat(config.getTags(), equalTo(true));
+        assertThat(config.getIgnoreUnsupportedTraits(), equalTo(true));
+    }
+}